ממשק JavaScript API חדש שיכול לעזור לכם להימנע מאיזון בין ביצועי העומסים לבין רמת התגובה לקלט.
קשה לטעון מהר. אתרים שמשתמשים ב-JS כדי להציג את התוכן שלהם צריכים כרגע לבצע פשרה בין ביצועי הטעינה לבין תגובתיות הקלט: לבצע את כל העבודה הנדרשת להצגה בבת אחת (ביצועי טעינה טובים יותר, תגובתיות קלט גרועה יותר) או לפצל את העבודה למשימות קטנות יותר כדי לשמור על תגובה מהירה לקלט ולצביעה (ביצועי טעינה גרועים יותר, תגובתיות קלט טובה יותר).
כדי למנוע את הצורך בתמורה הזו, Facebook הציעה והטמיעה את ה-API isInputPending()
ב-Chromium כדי לשפר את התגובה ללא פגיעה בביצועים. על סמך המשוב שקיבלנו מגרסת המקור לניסיון, ביצענו כמה עדכונים ב-API, ואנחנו שמחים להודיע שה-API מופיע עכשיו כברירת מחדל ב-Chromium 87.
תאימות דפדפן
isInputPending()
נשלחת בדפדפנים מבוססי Chromium החל מגרסה 87.
אף דפדפן אחר לא סימן כוונה לשלוח את ה-API.
רקע
רוב העבודה בסביבה העסקית של JS מתבצעת על שרשור יחיד: ה-thread הראשי. כך מקבלים מודל ביצוע חזק למפתחים, אבל חוויית המשתמש (במיוחד המהירות של התגובה) עלולה להיפגע באופן משמעותי אם הסקריפט פועל במשך זמן רב. לדוגמה, אם הדף מבצע הרבה עבודה בזמן שמופעל אירוע קלט, הדף לא יטפל באירוע הקלט של הקליק עד שהעבודה הזו תושלם.
השיטה המומלצת הנוכחית היא לטפל בבעיה הזו על ידי חלוקת ה-JavaScript לבלוקים קטנים יותר. בזמן הטעינה של הדף, הדף יכול להריץ קצת JavaScript, ואז להעביר את השליטה בחזרה לדפדפן. לאחר מכן הדפדפן יכול לבדוק את תור אירועי הקלט שלו ולראות אם יש משהו שהוא צריך לעדכן את הדף לגביו. לאחר מכן הדפדפן יכול לחזור להריץ את הקטעים של JavaScript ככל שהם מתווספים. זה עוזר, אבל הוא עלול לגרום לבעיות אחרות.
בכל פעם שהדף מעביר את השליטה חזרה לדפדפן, חולף זמן מה עד שהדפדפן בודק את תור אירועי הקלט, מעבד אירועים ובוחרים את בלוק ה-JavaScript הבא. הדפדפן מגיב לאירועים מהר יותר, אבל זמן הטעינה הכולל של הדף מאט. אם נבצע העברה מדי פעם, הדף נטען לאט מדי. אם אנחנו תרוויח בתדירות נמוכה יותר, ייקח לדפדפן זמן רב יותר להגיב לאירועי משתמשים, ואנשים מתוסכלים. לא כיף.
ב-Facebook, רצינו לראות איך דברים ייראו אם נמצא גישה חדשה לטעינה שתבטל את הוויתור המתסכל הזה. פנינו לחברים שלנו ב-Chrome בנושא הזה והצענו את ההצעה ל-isInputPending()
. ה-API של isInputPending()
הוא הראשון שמשתמשים במושג 'הפרעות' לקלט של משתמשים באינטרנט, והוא מאפשר ל-JavaScript
לבדוק קלט בלי להגיע לדפדפן.
מאחר שהיה עניין ב-API, שיתוף הפעולה עם הקולגות שלנו ב-Chrome הוביל להטמעה ולשחרור התכונה ב-Chromium. בעזרת המהנדסים של Chrome, השגנו את התיקונים לגרסת מקור לניסיון (שמאפשרת ל-Chrome לבדוק שינויים ולקבל משוב ממפתחים לפני השקה מלאה של API).
עכשיו הטמענו שינויים ב-API על סמך המשוב שקיבלנו מגרסת המקור לניסיון ומחברי קבוצת העבודה של W3C בנושא ביצועי האינטרנט.
דוגמה: תזמון תפוקה
נניח שאתם צריכים לבצע כמה פעולות לחסימת התצוגה כדי לטעון את הדף, למשל יצירת רכיבי עיצוב מרכיבים, חיסור של ראשוניים או פשוט ציור של גלגל טעינה מגניב. כל אחד מהם מחולק לפריט עבודה נפרד. בעזרת התבנית של מתזמן המשימות, נציג סקיצה של אופן העיבוד של העבודה בפונקציה היפותטית processWorkQueue()
:
const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
if (performance.now() >= DEADLINE) {
// Yield the event loop if we're out of time.
setTimeout(processWorkQueue);
return;
}
let job = workQueue.shift();
job.execute();
}
כשמפעילים את processWorkQueue()
מאוחר יותר במשימות מאקרו חדשות באמצעות setTimeout()
, הדפדפן יכול להמשיך להגיב במידה מסוימת לקלט (הוא יכול להריץ פונקציות לטיפול באירועים לפני שהעבודה ממשיכה) ועדיין להמשיך לפעול ללא הפרעה יחסית. עם זאת, יכול להיות שהתזמון יבוטל על ידי עבודה אחרת שדורשת שליטה על לולאת האירוע למשך זמן רב, או שזמן האחזור של האירוע יימשך עד QUANTUM
אלפיות שנייה נוספות.
זה בסדר, אבל אפשר לשפר? בהחלט!
const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
if (navigator.scheduling.isInputPending() || performance.now() >= DEADLINE) {
// Yield if we have to handle an input event, or we're out of time.
setTimeout(processWorkQueue);
return;
}
let job = workQueue.shift();
job.execute();
}
באמצעות קריאה ל-navigator.scheduling.isInputPending()
, אנחנו יכולים להגיב לקלט מהר יותר ועדיין להבטיח שהעבודה שלנו עם חסימת תצוגה תפעל ללא הפרעה אחרת. אם אנחנו לא רוצים לטפל בשום דבר מלבד קלט (למשל ציור) עד שהעבודה תושלם, אפשר גם להגדיל בקלות את האורך של QUANTUM
.
כברירת מחדל, אירועים 'מתמשכים' לא מוחזרים מ-isInputPending()
. למשל, mousemove
, pointermove
ועוד. אם ברצונך להעביר את הבעלות עליהם גם כן, אין בעיה. מספקים אובייקט ל-isInputPending()
עם הערך includeContinuous
שמוגדר כ-true
, וזהו:
const DEADLINE = performance.now() + QUANTUM;
const options = { includeContinuous: true };
while (workQueue.length > 0) {
if (navigator.scheduling.isInputPending(options) || performance.now() >= DEADLINE) {
// Yield if we have to handle an input event (any of them!), or we're out of time.
setTimeout(processWorkQueue);
return;
}
let job = workQueue.shift();
job.execute();
}
זהו! מסגרות כמו React מפתחות תמיכה ב-isInputPending()
בספריות הליבה שלהן לתזמון באמצעות לוגיקה דומה. אנחנו מקווים שהדבר יאפשר למפתחים שמשתמשים בפלטפורמות האלה ליהנות מ-isInputPending()
מאחורי הקלעים בלי צורך בכתיבת מחדש משמעותית.
כניעה לא תמיד היא דבר רע
חשוב לציין שניצול נמוך יותר הוא לא הפתרון הנכון לכל תרחיש לדוגמה. יש סיבות רבות להחזיר את השליטה לדפדפן, מלבד עיבוד אירועי קלט, למשל כדי לבצע עיבוד וגרפיקה ולהריץ סקריפטים אחרים בדף.
יש מקרים שבהם הדפדפן לא יכול לשייך בצורה נכונה אירועי קלט בהמתנה. באופן ספציפי, הגדרת קליפים ומסכות מורכבים ל-iframes ממקורות שונים עשויה לגרום לדיווח על תוצאות שליליות שגויות (כלומר, isInputPending()
עשוי להחזיר באופן בלתי צפוי את הערך false כשמטרגטים את התמונות האלה). אם האתר שלכם מחייב אינטראקציות עם תת-מסגרות מסוגננות, ודאו שאתם מניבים מספיק רווחים.
חשוב גם לשים לב לדפים אחרים שחולקים לולאת אירועים. בפלטפורמות כמו Chrome ל-Android, מקורות מרובים חולקים לעתים קרובות לולאת אירועים. הפונקציה isInputPending()
אף פעם לא תחזיר את הערך true
אם הקלט נשלח למסגרת בין מקורות. כתוצאה מכך, דפים עם רקע עלול להפריע לתגובתיות של דפים בחזית. כדאי להפחית, לדחות או לייצר בתדירות גבוהה יותר כשעובדים ברקע באמצעות Page Visibility API.
מומלץ להשתמש ב-isInputPending()
בתבונה. אם אין פעולה שחוסמת משתמשים, כדאי שתהיו אדיבים כלפי אחרים בלולאת האירוע ותעניקו להם בתדירות גבוהה יותר. משימות ארוכות עלולות להזיק.
משוב
- אפשר לשלוח משוב על המפרט במאגר is-input-pending.
- אפשר לפנות אל @acomminos (אחד ממחברי המפרט) ב-Twitter.
סיכום
אנחנו שמחים להשיק את isInputPending()
ומאפשרים למפתחים להתחיל להשתמש בו כבר היום. זו הפעם הראשונה ש-Facebook יצרה ממשק API חדש לאינטרנט, והובילה אותו משלב הרעיון ועד להצעה לתקן, ואז עד להשקה בפועל בדפדפן. אנחנו רוצים להודות לכל מי שעזר לנו להגיע לשלב הזה, ולשלוח קריאת תיגר מיוחדת לכל הצוות של Chrome שעזר לנו לפתח את הרעיון הזה ולהוציא אותו לפועל.
התמונה הראשית (Hero) היא של Will H McMahan ב-Unsplash.