
return רגיל, כי הפונקציה מסתיימת לפני שהתוצאה מוכנה. הפתרון הוא להעביר לפונקציה callback, כלומר פונקציה שהפעולה תקרא לה כשתסתיים.doWorkAsync מפעילה setTimeout ומנסה להחזיר את הסכום. ה-return נמצא בתוך פונקציית הטיימר ולא בסוף הפונקציה החיצונית, ולכן הפונקציה מחזירה undefined. Operation C מתבצעת לפני שהסכום מוכן.toFixed מקבל רק מספרים בין 0 ל-100. כשמעבירים מינוס 5, נזרקת שגיאה, והתוכנית קורסת אם אין טיפול בה. לכן עוטפים את הקוד הפנימי ב-try/catch, ובמקרה של שגיאה קוראים לפונקציית הכישלון.try/catch חייב להיות בתוך הפונקציה האסינכרונית. טריי-קאץ' שנמצא סביב הקריאה מבחוץ לא תופס שגיאה שקורית אחר כך בטיימר.הטקסט המלא של הדקות הראשונות בהרצאה «הסבר על Callback» בקורס הווידאו של PracticU – כתוביות בעברית עם זמן התחלה לכל פסקה. כך תוכלו לקרוא ולחפש את הנושאים לפני שמתחילים ללמוד.
אז ראינו איך אנחנו מחזירים מידע מפונקציה סינכרונית. בואו נשכפל את הפונקציה הזאת ונקרא לה DoWork Async. כעת, מה הבעיה?
אנחנו צריכים לגרום לפונקציה הזאת שיהיה בה איזשהו קטע קוד שלוקח את הקוד הכבד שיש לה ומזיז אותו הצידה ל-C++ APIs. לצורך כך אנחנו נבצע את מה שעשינו בסרטון הקודם, set timeout.
ונבוא ונגיד, תראה, הנה יש פה איזשהו קטע קוד, ויכול להיות 0 מילישניות, זה יכול להיות 10 מילישניות, זה לא משנה כל כך. ברגע שיש פה set timeout, הקוד הזה שאנחנו כותבים כאן בפונקציה זז הצידה למשך הזמן הזה, ואז מוחזר דרך ה-callback queue וה-event loop בחזרה ל-call stack, והנה הקטע קוד הכבד הוא נמצא פה ופה אנחנו
בעצם מסיימים את התהליך. למה מה שכתבתי עכשיו לא טוב? בואו רגע נשים את זה כאן, נעלה את זה לפה. חשפנו החוצה את הפונקציה וכל הקטע קוד הראשון הוא עבד כמו שצריך בלי בעיה.
אוקיי, נשים את כל זה רגע בהערה ואנחנו רוצים להשתמש עכשיו ב-Do Work Async. למה מה שכתבתי הוא לא בסדר?
אז בואו נסתכל מה יקרה. Operation A יתחיל לעבוד. Do Work Async תתחיל לעבוד. יוצג לנו על המסך Starting Operation B, וכל הקטע קוד הזה זז הצידה.
פה חסר לי רק נקודה פסיק. זז הצידה ומאפשר לקטע קוד הזה לרוץ. אוקיי, בינתיים הקטע קוד הזה רץ, אז הפונקציה הזאת הסתיימה.
אם הפונקציה הסתיימה, פונקציה בג'אווה סקריפט, לא משנה אם ג'אווה סקריפט client side או node.js, פונקציה מסתיימת ולא כתוב בסיום הפונקציה return, שימו לב שהreturn הזה לא כתוב בסיום הפונקציה, הוא כתוב בתוך משהו אחר, אז זה אומר שהפונקציה מחזירה undefined.
זה מה שפונקציה בג'אווה סקריפט מחזירה אם לא כתבנו לה להחזיר משהו אחר. זאת אומרת שמיידית הפונקציה הזאת תחזיר undefined. אז אנחנו נדפיס ברזולט undefined, לא הצלחנו לקבל בחזרה את הסכום.
ואז יוצג Operation C ולבסוף, אחרי החישוב הזה, יוצג Operation B ואז יוחזר return sum. לאן יוחזר return sum?
יוחזר return sum מהפונקציה הזאת. תשימו לב איפה אני נמצא כרגע. אני נמצא בתוך איזושהי פונקציית arrow function ששלחנו לסט טיים אאוט. מכאן החזרנו את הסאם, זה לא עוזר לי, זה לא החזרה של הסאם מחוץ לדו-וורק אסינק.
בואו נריץ את זה ונראה את כל ההצגות האלה. Node, App, Go, הנה אנחנו רואים Operation A, Starting Operation B, Result, Undefined, בדיוק כמו שאמרנו.
Operation C יכל לעבוד כמו שצריך, שזה היתרון, ו-Operation B הסתיים. מה שלא הצלחנו לעשות, זה לא הצלחנו להחזיר את סם בחזרה.
בואו נבדוק את זה שוב פעם. הופה, הנה אנחנו רואים שאנחנו ממתינים. Operation B מסתיים, לא הצלחנו להחזיר בחזרה. טוב, אז זו לא הטכניקה הנכונה.
אולי אני אעשה את זה. ברור שזה לא יעבוד. מה זה סם? סם לא מוגדר פה, הוא מוגדר פה. אתם רואים שהסם הזה בכלל, אפור, זה כמובן שלא יעבוד, והנה אני אפילו רואה שהוא אומר לי, sum is not defined, טוב, בסדר, בואו ניקח את sum ונוציא אותו לכאן.
עכשיו בטוח שהוא מכיר את sum, כי sum מוגדר בסקופ שלו, גם לא בסדר, מפני שהקוד הזה בכלל עוד לא התחיל להתבצע ואז הוא מחזיר לנו את sum.
מה יש ב-sum? אפס, בואו נבדוק את זה, ושימו לב שהוא אומר לי result, אפס, לא הצלחנו להחזיר בחזרה את הערך.
מה אנחנו בעצם צריכים לבצע? קודם כל, למה אני לא רואה Ending Operation B? או, יופי, Ending Operation B. אז בעצם מה בעצם אנחנו צריכים לעשות? אנחנו צריכים איכשהו לתאר, או ליתר דיוק לשלוח לאזור הזה שמשתמש בנו את התוצאה.
וזה מתבצע על ידי כמה טכניקות. אנחנו נתחיל עם Callback Function. callback function זה מצב שבו אנחנו מקבלים לסוגריים של הפונקציה שלנו פונקציית callback שברגע שהעבודה שלוקחת הרבה זמן ונמצאת כמובן בצד ב-c++ APIs מסתיימת היא קוראת לcallback function ונותנת לה את התוצאה אז אנחנו יכולים להחזיר את ה-let sum שווה 0 הזה לכאן אין לנו פה return בכלל
פה אנחנו מקבלים callback פונקציה. ברגע שסיימנו את העבודה, אני קורא לפונקציה, נותן לה תסכום. זה אומר שהאזור הזה שמשתמש בי, עכשיו לא יכול לקבל בחזרה את הסכום, אלא צריך להביא לי איזושהי פונקציה, ובתוכה אני אציג את התוצאה, והתוצאה זה ה-result.
בעצם מקודם קראנו לזה sum, אז אני אשאיר את השם sum. אז זה בעצם דוגמה מקבילה לדוגמה הזאת. הדוגמה הסינכרונית יכלה בקלות להחזיר תשובה מהפונקציה, ואז המשכנו והשתמשנו עם התשובה.
הדוגמה הסינכרונית לא יכלה להחזיר תשובה מהפונקציה, היינו חייבים לשלוח איזושהי פונקציה לתוך הפונקציה הזאת, לתוך ה-Do Work Async, וברגע שה-Do Work Async באמת יש לה את התשובה והיא מסתיימה, קוראת לפונקציה שלנו ונותנת לה את התשובה, אנחנו מקבלים את התשובה, עושים איתה מה שאנחנו
רוצים. בגלל זה הפונקציה הזאת נקראת callback, כי אני הראשון, הקוד הזה הוא הראשון שקורא ל-do work asking, ואז מה do work asking עושה?
He calls me back וקורא לפונקציה הזאת שהיא שלי ונותן את התשובה. כאן הצלחנו לעשות את זה כמו שצריך. בואו נראה. אם אנחנו מריצים את זה, נראה אופרשן A, סטארטינג אופרשן B, אופרשן C. למה?
מפני שזה מתחיל לעבוד אבל הוא זז הצידה ומאפשר לקוד הזה להמשיך ואז כשהוא מסתיים נראה אינדינג אופרשן B, קריאה לקולבאק, תפעיל את הפונקציה הזאת ואז נראה את הריזולט.
בואו ננקה את המסך ונריץ. הנה אנחנו רואים A סטארטינג B C, אינדינג B והנה הצלחנו להעביר את התשובה בחזרה. נשאר לטפל בדבר אחד ויחיד, מה קורה אם הקוד הזה קורס?
אם הקוד הזה קורס, יש לנו פה בעיה, מפני שאני לא באמת רוצה לטפל פה בקריסה, אני רוצה לטפל בה פה. יכול להיות מצב שאני הבאתי לקטע קוד הזה איזה משהו שהוא לא בסדר, וגם מה בעצם הקוד הזה אמור לעשות בנוגע לטיפול בבעיה.
הטיפול בבעיה תמיד צריך להיות מי שקורא לקטע קוד הבעייתי. ברוב המקרים, בוא נניח. איך אנחנו מטפלים בקריסות ב-JavaScript? קודם כל, בואו נייצר קריסה.
אז יש לנו כל מיני טכניקות לייצר קריסה ב-JavaScript. בואו ניקח דוגמה פשוטה. נניח שכאן אנחנו לוקחים ת-SAM ואנחנו מכניסים לתוכו עיגול, כלומר, אנחנו מעגלים את סם ל-5 ספרות בלבד.
או 5 ספרות, או 5 ספרות אחרי הנקודה העשרונית. בואו ננסה רגע את זה, נדמה לי ש-5 ספרות אחרי הנקודה העשרונית, אם אני לא טועה.
זה כמובן אמור לעבוד כמו שצריך. בואו נבדוק את זה רגע. אז יש לנו כאן מאוד אפ, גו, תמתין לו. אוקיי, הנה אנחנו רואים את ה-5 ספרות אחרי הנקודה העשרונית, זה בסדר.
זהו, לא קרס, הכל בסדר, הוא הסתיים. למה שיקרוס? אמרתי לו 5 ספרות אחרי הנקודה העשירונית. אבל בואו נניח שבטעות קיבלתי את המספר הזה מהיוזר, טענתי אותו מקובץ, לא יודע מה, ובטעות הוא מינוס 5.
עכשיו אנחנו צריכים איכשהו לטפל בבעיה. כרגע תהיה לנו קריסה, כי 2Fixed תגרום לקריסה של התוכנית, מפני שהיא לא מוכנה לקחת את המינוס 5 בתור ארגומנט.
בואו נריץ את זה, ונמתין, והופה, יש לנו קריסה. To fix DG's target must be between 0 and 100 ואנחנו לא רוצים שתהיה לנו קריסה ב-Node.js אנחנו רוצים כמובן לתפרוס את הבעיה ולטפל בה כמו שצריך.
אתם יכולים להגיד לי רגע אבל מה הבעיה לעשות פה טרייקאץ' ולטפל בבעיה? אז אני כן רוצה לעשות טרייקאץ' אבל אני לא רוצה לעשות את הטרייקאץ' כאן אני רוצה לעשות את הטרייקאץ' כאן והבעיה היא, או הסיבה היא, שאני רוצה לעשות את טרייקאץ' כן, כי הרבה פעמים קורה שאני מעביר את הפרמטר הזה לפונקציה מבחוץ.
נניח כאן אני רוצה לראות פיקס משהו כזה, נשים פה את הפיקס ונשלח כאן מינוס חמש. עכשיו, אם אני אריץ את זה כמובן שתהיה קריסה, אבל מי שבאמת היה לא בסדר זה הקטע קוד הזה, אולי המינוס חמש הזה יתקבל מהיוזר, אולי המינוס חמש הזה יתקבל מקובץ.
הקטע קוד הזה לא יודע למה הוא קיבל את המינוס חמש, אז הוא לא יכול לטפל באמת בבעיה. הוא חייב איכשהו לציין שיש בעיה לקטע קוד הזה.
מה קורה עם טריי קאץ'? בואו נשים כאן טריי קאץ'. in a try ו-in a catch, in a error, console.log.error.message ונראה שהבעיה טופלה.
אם תהיה לנו קריסה או הרי כאן יש לנו את ה-crash אז אנחנו כמובן ניתפס ב-catch כי אנחנו יודעים שכל crash מוביל אותנו ל-catch והנה נטפל בבעיה, זה הטיפול, מה הבעיה? היוזר לא הביא לי מספר נכון כאן במינוס 5 הזה אבל זה לא יעבוד.
למה זה לא יעבוד? מפני שהקוד הזה הוא אסינכרוני. אז אם הקוד הזה הוא אסינכרוני, התחלנו את Operation A, התחלנו את הקטע קוד, הקוד הזה עדיין לא הסתיים, הוא תקוע איפשהו פה, סיימנו את Operation C, ויצאנו לגמרי מהטראי, ביי ביי, עברנו הלאה.
כאשר נגיע לקטע קוד הזה והוא יקרוס, הוא לא יחזור מפה בחזרה ל-catch. בואו נראה איך זה מתבצע. נריץ את זה, הנה יש לנו את ההתחלה, והקוד קרס.
אפילו שאתם יכולים להגיד, רגע, אבל הקטע קוד הזה נמצא איפשהו בתוך טרייקאץ'. טרייקאץ' בשיטה הקלאסית לא עובדת על קוד אסינכרוני.
אם היינו עושים בתוך הפונקציה טרייקאץ' זה היה בסדר, אבל זה לא המצב שאני רוצה. אני רוצה את הטרייקאץ' כאן. פה טרייקאץ' בהחלט, או ליתר דיוק כאן, טרייקאץ' בהחלט היה תופס את הקריסה הזאת, מפני שמפה עד לכאן הקוד הזה הוא סינכרוני בצורה רגילה, אבל אני בעצם רוצה פה לתפוס את הבעיה.
אז מה אנחנו צריכים לעשות בשביל לטפל בבעיה כזאת כאשר אנחנו עובדים עם callbacks? מה שאנחנו צריכים לעשות זה להבין שקטע קוד אסינכרוני יכול להצליח, וקטע קוד אסינכרוני יכול להיכשל.
לכן, אם אנחנו מצליחים, אנחנו נמצאים כאן. אז אנחנו רוצים לקרוא לפונקציה שנקראת success callback. בואו נקרא לה בצורה כזאת. ואם אנחנו נכשלים, אנחנו צריכים לקרוא לפונקציה שנקראת error callback.
בואו עכשיו נצליח לקרוא לפונקציה הזאת בלי קריסה. מה בעצם המודל שמאפשר לנו לעשות את זה? אז המודל אומר כזה דבר. הפונקציה הסינכרונית צריכה לקבל pay פונקציות success callbacks ו-error callbacks.
דרך אגב, ב-Node.js תמיד ה-Error Callback נשלחת ראשונה. לכן, אני אעשה את זה גם כן.