ניפוי באגים ב-JavaScript אסינכרוני באמצעות כלי הפיתוח ל-Chrome

פרל צ'ן

מבוא

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

למרבה המזל, עכשיו ב-Chrome DevTools, ניתן לראות את מחסנית הקריאות המלאה של קריאות חוזרות (callback) אסינכרוניות ב-JavaScript!

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

אחרי שתפעילו את התכונה של מחסנית קריאות אסינכרונית בכלי הפיתוח, תוכלו להציג נתונים על המצב של אפליקציית האינטרנט שלכם בנקודות זמן שונות. כדאי ללכת בעקבות הסטאק המלא בשביל חלק מה-event listener, setInterval, setTimeout, XMLHttpRequest, Promise, requestAnimationFrame, MutationObservers ועוד.

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

בואו נאפשר את התכונה הזו ונבחן כמה מהתרחישים האלה.

הפעלת ניפוי באגים אסינכרוני ב-Chrome

רוצה לנסות את התכונה החדשה הזו? אפשר להפעיל אותה ב-Chrome. עוברים לחלונית מקורות ב-Chrome Canary DevTools.

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

מפעילים או משביתים את התכונה האסינכרונית.

תיעוד אירועי טיימר שעוכבו ותגובות XHR

ודאי ראיתם את זה כבר ב-Gmail:

Gmail מנסה שוב לשלוח אימייל.

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

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

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

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

לפני
נקודת העצירה (breakpoint) מוגדרת בדוגמה לדוגמה של Gmail בלי ערימות של שיחות אסינכרוניות.
החלונית של מחסנית השיחות ללא אסינכרונית.

כאן אפשר לראות ש-postOnFail() הופעל מקריאה חוזרת של AJAX, אבל אין מידע נוסף.

אחרי
נקודת העצירה (breakpoint) מוגדרת כדוגמה של Gmail לדוגמה עם מקבצים אסינכרוניים של שיחות.
חלונית Call Stack עם אסינכרוני.

כאן ניתן לראות ש-XHR התחיל מ-submitHandler(). איזה יופי!

אם מפעילים את מקבץ השיחות האסינכרוני, אפשר להציג את כל מקבץ השיחות כדי לבדוק בקלות אם הבקשה נשלחה מ-submitHandler() (אחרי שלוחצים על לחצן השליחה) או מ-retrySubmit() (שמתרחש אחרי עיכוב של setTimeout()):

submitHandler()
נקודת עצירה (breakpoint) מוגדרת בדוגמה מדומה של Gmail עם מקבצים אסינכרוניים של שיחות
retrySubmit()
נקודת עצירה (breakpoint) נוספת מוגדרת בדוגמה מדומה של Gmail עם מקבצים אסינכרוניים של שיחות

ביטויי צפייה באופן אסינכרוני

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

דוגמה לשימוש בביטויי שעון עם מקבצים של קריאות ל-aysnc

הערכת קוד מהיקפים קודמים

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

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

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

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

פותרים את ההבטחות השרשרת

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

זו אנימציה קטנה של הליכה בין ערימות הקריאות, בדוגמה async-best-example.html של ג'ייק.

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

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

אחרי
דוגמה לנקודת עצירה (breakpoint) בהבטחות עם מקבצים אסינכרוניים של קריאות.
חלונית Call Stack עם אסינכרוני.

וואו! הבטחות כאלה. הרבה קריאות חוזרות.

איך מקבלים תובנות על האנימציות באינטרנט

בואו נתעמק בארכיונים של HTML5Rocks. זוכרים את פול לואיס Leaner, Meaner, Faster Animations with requestAnimationFrame?

פותחים את ההדגמה של requestAnimationFrame ומוסיפים נקודת עצירה בתחילת השיטה update() (בערך בשורה 874) של post.html. בעזרת מקבצים אסינכרוניים של קריאות, אנחנו מקבלים הרבה יותר תובנות לגבי requestAnimationFrame, כולל היכולת ללכת את כל הדרך חזרה לקריאה החוזרת (callback) של אירוע גלילה שהופעל.

לפני
נקודת העצירה (breakpoint) מוגדרת בדוגמה requestAnimationFrame ללא ערימות קריאות אסינכרוניות.
החלונית של מחסנית השיחות ללא אסינכרונית.
אחרי
נקודת העצירה (breakpoint) מוגדרת בדוגמה requestAnimationFrame עם מקבצים של קריאות אסינכרוניות
ועם הפעלת האסינכרוני.

מעקב אחר עדכוני DOM כשמשתמשים ב-MutationObserver

MutationObserver מאפשר לנו לראות שינויים ב-DOM. בדוגמה הפשוטה הזו, כשלוחצים על הלחצן, צומת DOM חדש מצורף ל-<div class="rows"></div>.

הוספת נקודת עצירה (breakpoint) בתוך nodeAdded() (שורה 31) ב-demo.html. כשמפעילים ערימות קריאות אסינכרוניות, עכשיו אפשר להעביר את מחסנית הקריאות בחזרה דרך addNode() לאירוע הקליק הראשוני.

לפני
נקודת העצירה (breakpoint) מוגדרת בדוגמה של mutationObserver בלי ערימות קריאות אסינכרוניות.
החלונית של מחסנית השיחות ללא אסינכרונית.
אחרי
נקודת העצירה (breakpoint) מוגדרת בדוגמה של mutationObserver עם ערימות קריאות אסינכרוניות.
ועם הפעלת האסינכרוני.

טיפים לניפוי באגים ב-JavaScript בערימות קריאות אסינכרוניות

מתן שם לפונקציות

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

לדוגמה, ניקח פונקציה אנונימית כך:

window.addEventListener('load', function() {
  // do something
});

ותן לה שם כמו windowLoaded():

window.addEventListener('load', function <strong>windowLoaded</strong>(){
  // do something
});

כשאירוע הטעינה מופעל, הוא יופיע בדוח הקריסות של DevTools עם שם הפונקציה שלו, במקום "(anonymous function)" (פונקציה אנונימית) המוצפן. ככה הרבה יותר קל לראות מה קורה בדוח הקריסות בדוח הקריסות.

לפני
פונקציה אנונימית.
אחרי
פונקציה בעלת שם

המשך למידה

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

  • טיימרים: חשוב לחזור למקום שבו הופעלו setTimeout() או setInterval().
  • XHRs: צריך ללכת בחזרה למקום שבו קראו ל-xhr.send().
  • פריימים של אנימציה: חשוב לחזור אחורה אל המקום שבו קראו ל-requestAnimationFrame.
  • הבטחות: לחזור למקום שבו קיימת ספק שהובטח.
  • Object.observe: עכשיו חוזרים למקום שבו הקריאה החוזרת (callback) הייתה אל היעד המקורי של הצופה.
  • MutationObservers: חשוב לחזור למקום שבו הופעל אירוע הצופה עם המוטציה.
  • window.postMessage(): מעבר על קריאות להעברת הודעות בתוך התהליך.
  • DataTransferItem.getAsString()
  • ממשק API של FileSystem
  • IndexedDB
  • WebSQL
  • אירועי DOM כשירים דרך addEventListener(): חוזרים למקום שבו האירוע הופעל. בגלל סיבות שקשורות לביצועים, לא כל אירועי ה-DOM עומדים בדרישות לשימוש בתכונה 'ערימות קריאות אסינכרוניות'. דוגמאות לאירועים שזמינים כרגע: 'scroll', 'hashchange' ו-'selectionchange'.
  • אירועי מולטימדיה דרך addEventListener(): חוזרים למקום שבו האירוע הופעל. אירועי מולטימדיה זמינים כוללים: אירועי אודיו ווידאו (למשל 'play', 'pause', 'ratechange'), אירועי WebRTC MediaStreamTrackList (למשל addtrack, 'removetrack') ואירועי MediaSource (למשל 'sourceopen').

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

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