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

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

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

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

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

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

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

איך להתייחס להשהיה לאחר קלט

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

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

איך לצמצם את זמן התגובה של המקלדת

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

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

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

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

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

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

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

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

הימנעות ממשימות שנמשכות הרבה זמן

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

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

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

שימו לב לאינטראקציות חופפות

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

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

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

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

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

סיכום

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