אופטימיזציה של השהיה לאחר קלט

למדו מהו עיכוב קלט, ולמדו טכניקות להפחתה שלו כדי שהאינטראקציה תהיה מהירה יותר.

אינטראקציות באינטרנט הן עניין מורכב, ומתרחשת כל מיני סוגי פעילות בדפדפן כדי לעודד אותן. עם זאת, מה שמשותף לכולן הוא שהן יצטרכו להמתין זמן מה עד שהקריאות החוזרות של האירועים יתחילו לפעול. במדריך הזה נסביר מהו עיכוב קלט ומה אפשר לעשות כדי לצמצם אותו כדי שהאינטראקציות באתר יפעלו מהר יותר.

מהו עיכוב בקלט?

השהיית קלט היא פרק הזמן שעובר מהרגע שבו המשתמש יוצר אינטראקציה ראשונה עם הדף – כמו הקשה על מסך, לחיצה עם עכבר או הקשה על מקש – עד לרגע שבו הקריאות החוזרות של האירוע עבור האינטראקציה מתחילות. כל אינטראקציה מתחילה עם עיכוב מסוים בקלט.

תצוגה חזותית פשוטה יותר של העיכוב בקלט. בצד שמאל מופיעה ציור קו של סמן העכבר עם גלים כוכבים מאחוריו, שמסמל את התחלת האינטראקציה. בצד שמאל מופיע קו של גלגל השיניים, שמציין מתי הגורמים המטפלים באירועים של אינטראקציה מתחילים לפעול. הרווח שביניהם מצוין כעיכוב הקלט עם סוגריים מסולסלים.
המכניקה של השהיית הקלט. כשקלט מתקבל על ידי מערכת ההפעלה, יש להעביר אותו לדפדפן לפני שהאינטראקציה מתחילה. התהליך הזה לוקח קצת זמן, ואפשר להאריך אותו באמצעות עבודה קיימת ב-thread הראשי.

חלק מסוים מהשהיית הקלט הוא בלתי נמנע: תמיד לוקח למערכת ההפעלה פרק זמן מסוים לזהות אירוע קלט ולהעביר אותו לדפדפן. עם זאת, חלק זה של העיכוב בקלט הוא בדרך כלל לא מורגש, ויש דברים אחרים שקורים בדף עצמו שעלולים לגרום לעיכובים בקלט למשך זמן רב מספיק כדי לגרום לבעיות.

מה המשמעות של העיכוב בקלט

באופן כללי, מומלץ להקפיד שכל אינטראקציה תהיה קצרה ככל האפשר כדי שהאתר יעמוד בסף 'טוב' של המדד מאינטראקציה ישירה (INP), ללא קשר למכשיר של המשתמש. בדיקת עיכובים בקלט היא רק חלק אחד בעמידה בסף הזה.

לכן כדאי לשאוף לעיכוב הקלט הקצר ביותר האפשרי כדי להגיע לסף 'טוב' של INP. עם זאת, חשוב לשים לב שלא צפויים לבטל לגמרי את העיכובים בקלט. כל עוד יש להימנע מעבודה מיותרת ב-thread הראשי בזמן שהמשתמשים מנסים ליצור אינטראקציה עם הדף, העיכוב בקלט אמור להיות נמוך מספיק כדי למנוע בעיות.

איך לצמצם את ההשהיה בקלט

כפי שצוין קודם, אי אפשר להימנע מעיכוב מסוים בקלט, אבל מצד שני, אפשר להימנע מעיכוב מסוים בקלט. ריכזנו כאן כמה נקודות שכדאי לקחת בחשבון אם נתקלתם בעיכובים ארוכים בקלט.

לא מומלץ להשתמש בטיימרים חוזרים שגורמים לעבודה ארוכה מדי ב-thread הראשי

יש שתי פונקציות טיימר נפוצות ב-JavaScript שיכולות לתרום לעיכוב בקלט: setTimeout ו-setInterval. ההבדל בין השניים הוא שב-setTimeout מתזמנים קריאה חוזרת (callback) כך שתרוץ אחרי פרק זמן מסוים. מצד שני, setInterval מתזמנ/ת קריאה חוזרת כך שתפעל כל n אלפיות השנייה באופן קבוע, או עד שהטיימר מופסק בזמן ההפעלה של clearInterval.

setTimeout לא בעייתי בפני עצמו - למעשה, הוא יכול לעזור בהימנעות ממשימות ארוכות. עם זאת, תלוי מתי הזמן הקצוב מסתיים, ואם המשתמש מנסה ליצור אינטראקציה עם הדף כאשר הזמן הקצוב לתפוגה יסתיים.

בנוסף, ניתן להריץ את setTimeout בלולאה או באופן רקורסיבי, כשהפעולה תהיה דומה יותר ל-setInterval, אבל עדיף לא לתזמן את האיטרציה הבאה עד להשלמת החזרה הקודמת. כלומר, הלולאה תיתן ל-thread הראשי בכל פעם שאתם מפעילים את setTimeout, אבל חשוב לוודא שהקריאה החוזרת לא גורמת למאמץ מיותר.

setInterval מבצע קריאה חוזרת (callback) במרווחי זמן, ולכן יש סיכוי גבוה בהרבה שהוא יפריע לאינטראקציות. הסיבה לכך היא שבניגוד למופע יחיד של קריאה חוזרת ל-setTimeout, שהיא קריאה חוזרת וחד-פעמית שעשויה להפריע לאינטראקציה של המשתמש – האופי החוזר של setInterval גורם לכך שיש סיכוי גבוה בהרבה שהוא יפריע לאינטראקציה, ולכן יש עליה עיכוב בקלט של האינטראקציה.

צילום מסך של הכלי לניתוח ביצועים (profiler) בכלי הפיתוח ל-Chrome שמדגים את ההשהיה בקלט. משימה שמופעלת על ידי פונקציית טיימר מתרחשת ממש לפני שהמשתמש מתחיל אינטראקציה עם קליק. עם זאת, הטיימר מאריך את עיכוב הקלט, וגורם לקריאות חוזרות לאירועים של האינטראקציה לפעול מאוחר יותר מכפי שהיה מתבצע אחרת.
טיימר שנרשם על ידי שיחה קודמת ב-setInterval שתרמו לעיכוב בקלט, כפי שמתואר בחלונית הביצועים של כלי הפיתוח ל-Chrome. העיכוב הנוסף בקלט גורם להפעלות החוזרות של האירועים של האינטראקציה לפעול מאוחר יותר מכפי שהן היו יכולות לפעול.

אם פועלים טיימרים בקוד מאינטראקציה ישירה (First-Party), יש לכם שליטה עליהם. הערך אם אתה זקוק להם, או עשה כמיטב יכולתך לצמצם את העבודה בהם ככל האפשר. עם זאת, השימוש בטיימרים בסקריפטים של צד שלישי הוא שונה. לעיתים קרובות אין לכם שליטה על הפעולות שסקריפט של צד שלישי עושה. כדי לפתור בעיות בביצועים בקוד של צד שלישי, לעיתים קרובות צריך לעבוד עם בעלי עניין כדי לקבוע אם סקריפט מסוים של צד שלישי הוא הכרחי. אם כן, עליכם ליצור קשר עם ספק סקריפטים של צד שלישי כדי לקבוע מה אפשר לעשות כדי לתקן בעיות בביצועים שהן עלולות לגרום לאתר שלכם.

כדאי להימנע ממשימות ארוכות

אחת הדרכים לצמצם עיכובים ארוכים בקלט היא להימנע ממשימות ארוכות. כשיש כמות גדולה מדי של עבודה ב-thread הראשי שחוסמת את ה-thread הראשי במהלך אינטראקציות, היא גורמת לעיכוב בקלט לפני שהמשימות הארוכות יסתיימו.

תצוגה חזותית של משך הזמן שמשימות מאריכות את עיכוב הקלט. בחלק העליון של הדף, אינטראקציה מתרחשת זמן קצר אחרי שמשימה ארוכה אחת מופעלת. הפעולה הזו גורמת לעיכוב משמעותי בקלט, שגורם לקריאות חוזרות לאירועים לפעול זמן רב יותר מהמתוכנן. בחלק התחתון, אינטראקציה מתרחשת בערך באותו זמן, אבל המשימה הארוכה מתחלקת למספר משימות קטנות יותר. הפעולה הזו מאפשרת להפעלות החוזרות של האירועים של האינטראקציה לפעול הרבה יותר מהר.
תצוגה חזותית של מה שקורה לאינטראקציות כאשר המשימות ארוכות מדי והדפדפן לא יכול להגיב מהר מספיק לאינטראקציות, לעומת כאשר משימות ארוכות יותר מחולקות למשימות קטנות יותר.

נוסף על צמצום כמות העבודה שאתם עושים במשימה מסוימת - ותמיד כדאי לשאוף לבצע כמה שפחות עבודה ב-thread הראשי – כדי לשפר את התגובה לקלט של המשתמשים, תוכלו לחלק משימות ארוכות.

מודעות חופפת לאינטראקציות

חלק מאתגר במיוחד באופטימיזציה של INP יכול להיות אם יש אינטראקציות חופפות. המשמעות של חפיפה באינטראקציות היא שאחרי האינטראקציה שלכם עם רכיב אחד, אתם מבצעים אינטראקציה נוספת עם הדף לפני שלאינטראקציה הראשונית הייתה הזדמנות להציג את הפריים הבא.

תיאור שמראה מתי משימות יכולות לחפוף כדי לגרום לעיכובים ארוכים בקלט. בתיאור הזה, אינטראקציה של קליק חופפת לאינטראקציה באמצעות Keydown כדי להגדיל את ההשהיה בקלט של האינטראקציה עם Keydown.
המחשה חזותית של שתי אינטראקציות בו-זמניות בכלי לניתוח ביצועים (Performance profiler) בכלי הפיתוח של Chrome. עבודת העיבוד באינטראקציה הראשונית של הקליק גורמת לעיכוב בקלט של האינטראקציה הבאה עם המקלדת.

מקורות החפיפה של האינטראקציות עשויים להיות פשוטים כמו משתמשים שמבצעים אינטראקציות רבות בפרק זמן קצר. מצב זה עשוי להתרחש כאשר משתמשים מקלידים בשדות בטופס, שבהם אינטראקציות רבות עם המקלדת יכולות להתרחש בפרק זמן קצר מאוד. אם העבודה על אירוע מרכזי היא יקרה במיוחד, כמו במקרה הנפוץ של שדות השלמה אוטומטית שבהם מתבצעות בקשות רשת לקצה העורפי, יש לכם כמה אפשרויות:

  • שקלו לבטל את היציאה משדות הקלט כדי להגביל את מספר הפעמים שקריאה חוזרת (callback) מבוצעת בפרק זמן נתון.
  • אפשר להשתמש ב-AbortController כדי לבטל בקשות fetch יוצאות, כך שה-thread הראשי לא יהיה עמוס בטיפול בקריאות חוזרות של fetch. הערה: אפשר להשתמש במאפיין signal של מופע AbortController גם כדי לבטל אירועים.

מקור נוסף לעיכוב מוגבר בקלט כתוצאה מאינטראקציות חופפות יכול להיות אנימציות יקרות. באופן ספציפי, אנימציות ב-JavaScript עשויות להפעיל קריאות רבות של requestAnimationFrame, שעלולות להפריע לאינטראקציות של המשתמשים. כדי לעקוף את הבעיה, השתמשו באנימציות CSS ככל האפשר כדי להימנע מהוספה של מסגרות אנימציה שעלולות להיות יקרות בתור. אבל אם עושים זאת, כדאי להימנע מאנימציות לא מורכבות, כך שהאנימציות יפעלו בעיקר בשרשורים של ה-GPU ושל המחבר, ולא ב-thread הראשי.

סיכום

עיכובים בקלט עשויים שלא לייצג את רוב הזמן שלוקח לאינטראקציות שלכם, אבל חשוב להבין שלכל חלק באינטראקציה נדרש זמן שאתם יכולים לקצר. אם הבחינו בעיכוב ארוך בקלט, יש לכם הזדמנויות לצמצם אותו. כדאי להימנע מקריאות חוזרות (callback) חוזרות של טיימרים, מפיצול משימות ארוכות ומוּדעוּת לחפיפה פוטנציאלית באינטראקציות, כל אלה עוזרים לצמצם את ההשהיה בקלט, וכך להגביר את האינטראקטיביות של משתמשי האתר.

תמונה ראשית (Hero) מתוך UnFlood, מאת Erik Mclean.