JS: Uncaught (in promise) - מתי קורה?
-
משהו חדש שלמדתי היום, (מקור)
מה הפלט בכל אחת מהסְנִיפְּטִים הבאים, ולמה?
(שלושת הראשונים אמורים להיות פשוטים למרות שגם שם לא ידעתי את התשובה הנכונה, האחרון היה חידוש בשבילי ומן הסתם יהיה חידוש להרבה אנשים)
1
(async () => { return Promise.reject("נו!"); })().catch(e => { console.log('אויש!'); });
2
(async () => { const promise = Promise.reject("נו!"); })().catch(e => { console.log('אויש!'); });
3
(async () => { const promise = Promise.reject("נו!"); await promise; })().catch(e => { console.log('אויש!'); });
4
(async () => { const promise = Promise.reject("נו!"); await new Promise(resolve => setTimeout(resolve, 1000)); await promise; })().catch(e => { console.log('אויש!'); });
-
הנה התשובות וההסברים
הקדמה: כללו של דבר, אם אתה לא מטפל ב-rejection של promise תוך כדי אותה איטרציה של ה-event loop - מתקבלת אזהרה של
Uncaught (in promise)
בקונסול.איך מטפלים ב-rejection? מצמידים catch ל-promise שמרודז'ק (rejected...).
מתי נגמר איטרציה של ה-event loop? כאשר לא נשאר יותר קוד סינכרוני להריץ.
1
הפלט:
אויש!
.
למה? פשוט, כי החזרת את הפרומיס המרודז'ק לקורא של הפונקציה האסינכרונית, והוא הצמיד לו catch תוך כדי אותו איטרציה של ה-event loop.2
הפלט:
Uncaught (in promise) נו!
למה? הרי הצמדת catch לפונקציה האסינכרונית?
התשובה: הפרומיס שהפונקציה האסינכונית מחזירה לא קשורה לפרומיס שיצרת בקריאתPromise.reject
.3
הפלט:
אויש!
כאן מילת הפתחawait
גורם לשני דברים לקרות.
א) זו הצהרה שההמשך של הפונקציה היא handler לפרומיס שעליו אתה עושה await. כך שהמשך הקוד שאחרי ה-await מתווספת לשלשלת הפרומיסים.
ב) הפרומיס שהפונקציה האסינכרונית מחזירה הוא התוצאה של ה-handler.
אם משהו ישתבש בפרומיס הראשון (Promise.reject("נו!")
) וזה לא מטופל ב-handler על ידי catch, או אם משהו ישתבש בשני (= כל קוד שאחרי שורת ה-await) זה יגרום לפרומיס המוחזר להיות מרודז'ק, דבר שייתפס על ידי ה-catch שהצמדת לו בתוך אותה איטרציה של ה-event loop.4
הפלט:
Uncaught (in promise) נו! אויש!
למה?
כי ה-await הראשון מסיים איטרציה של ה-event loop על ידי קריאה ל-setTimeout
. ה-rejection לא טופל בתוך האיטרציה אז מתקבל אזהרה:Uncaught (in promise) נו!
באיטצריה עתידית אתה קורא ל-await promise
שכנ"ל גורם לפורמיס המוחזר מהפונקציה להיות משולשל לפרומיס הראשון. ועליו אתה מצמיד catch, ברגע זה טיפלת ב-rejection.שים לב למשהו מעניין בקונסול של כרום, ברגע הראשון האזהרה של
Uncaught (in promise) נו!
מופיע באדום. אחרי שנייה כאשר ה-setTimeout חוזרת ומוצמד handler ל-rejection זה נהפך לשחור. -
עוד משהו קטן נוסף:
אם בדוגמה מס' 4, במקוםsetTimeout
של 1000 היינו עושים timeout של 0, אז לא מקבלים אזהרה שלUncaught (in promise)
.
דבר זה אפשר ללמוד גם מזה ש-await עצמו תמיד רץ אחרי שאר הקוד כאילו הרצת אותו על ידי setTimeout של 0, ומכל מקום אם היינו עושים await אחר בלי setTimeout לא היינו מקבלים אזהרה.כמובן אשמח מאוד לקבל הערות הארות ודעות סותרות, כל הארה והערה של אחרים יעזור ללבן את הנושא מעוד פנים.
-
@אהרן ב2 הפונקציה לא מחזירה את הפרומייז הפנימי ממילא הcatch שאחריו מקבל void.
הוא נשאר אם כן ללא טיפול, ללא catch אחריו.
המשונה זה 4 שמראה שני דברים:
א. גם עם יש שרשור יפה תחבירית בעצם מאחורי הקלעים לוקחי זמן עד שהcatch נרשם ממילא יש שגיאה
ב. גם אחרי שנה אפשר לרשום catch לפרומייז שנכשל ממזמן ו"למחוק" את השגיאה (ממש פלאי פלאים...)הנה המחשה יותר חזקה: https://codepen.io/dlt/pen/pojYMKR?editors=1010
אתה יכול לראות בקונסול שהלחיצה על הכפתור יש לה בחינה של חזרה בתשובה... -
@dovid אמר בJS: Uncaught (in promise) - מתי קורה?:
אתה יכול לראות בקונסול שהלחיצה על הכפתור יש לה בחינה של חזרה בתשובה...
בפיירפוקס השגיאה לא נמחקת, רק ידו של כרום פתוחה לקבל שבים...