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

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

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

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

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

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

איך לחשוב על השהיה לאחר קלט

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

חשוב לשים לב לחפיפה בין אינטראקציות

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

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

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

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

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

סיכום

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

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