
setTimeout של 5 שניות: הפונקציה עוברת ל-Web APIs, ואחרי הזמן עוברת לתור. היא רצה רק כשה-Call Stack פנוי. בינתיים C מתבצעת.הטקסט המלא של הדקות הראשונות בהרצאה «סימולטור של תכנות אסינכרוני ב-JS» בקורס הווידאו של PracticU – כתוביות בעברית עם זמן התחלה לכל פסקה. כך תוכלו לקרוא ולחפש את הנושאים לפני שמתחילים ללמוד.
אז אחרי שראינו בעצם איזשהו preview כזה לגבי מה זה Asynchronous Programming, בואו ניכנס ונחפש איזשהו סימולטור ספציפי שקיים, שמראה לנו בצורה קצת מעניינת כזאת, מה זה בדיוק ה-Asynchronous Programming של JavaScript, המודל הזה, המודל האסינכרוני של JavaScript.
אז בואו נחפש JavaScript Loop, Loop זה שרוכית מגדלת, ויש איזה בחור שנקרא פיליפ רוברטס, נכון? זה מה שהיה כתוב שם, שהוא בעצם בנה איזשהו סימולטור, הנה הבחורצ'יק הזה, שווה מאוד לראות את הסרטון הזה כי הוא מסביר שם בצורה ממש יפה מה שאני הולך להראות כרגע.
והסימולטור הזה מאפשר לנו להבין מהם החלקים של JavaScript ואיך היא בעצם מריצה את הקוד שלנו, כאשר זה מאוד מאוד דומה ל-Node.js. אז בואו נסתכל רגע.
אם אנחנו כרגע רוצים להריץ איזשהו קטע קוד, יש לנו פה איזשהו קטע קוד אבל הוא קצת מורכב לענייננו אז אני כרגע אמחק אותו ואני פשוט אגש ואעתיק את שלושת השורות האלה שאנחנו כתבנו ואדביק אותן פה בסימולטור.
יש לנו פה, תריץ בבקשה איזושהי פעולה a, פעולה b, פעולה c. בג'אווה סקריפט יש ארבעה חלקים ליתר דיוק כל מערכת ג'אווה סקריפט בקליינט סייד מכילה ארבעה חלקים ואלה החלקים שאנחנו רואים כאן.
החלק הראשון נקרא call stack זה בעצם הסינגל פרדד של ג'אווה סקריפט כל הקודים שאנחנו כותבים בעצמנו בג'אווה סקריפט מורצים פה כל החישובים כל העלולאות כל התנאים כל הקריאות לפונקציות הכל מתבצע אך ורק בטרד האחד והיחיד.
אז אם אנחנו מבצעים איזושהי פעולה שלוקחת הרבה זמן, מן הסתם היא תתקע את כל הפעילות של JavaScript. עכשיו יש פה עוד חלקים אבל שהדפדפן עצמו ב-JavaScript נותן לנו.
החלק הזה מצד ימין נקרא Web APIs. זה בעצם שירותים שהדפדפן נותן, ש-JavaScript יכולה להשתמש בהם, שבעצם מאפשרים לבצע איזשהו קטע קוד אבל בנפרד מה-call stack.
תכף אנחנו נראה למה הכוונה בקטע קוד ואיך אנחנו יכולים להריץ כאן קטע קוד בנפרד. עכשיו כל הקטע קוד הזה שרץ בנפרד כן רץ במחשב בטרד נפרד, אבל הטרד הנפרד הזה הוא לא מבחינת JavaScript אלא הוא שירות שהדפדפן מספק לנו.
שוב, אני מראה את כל הסימולציה הזאת על JavaScript Client Side. ותכף נראה מה קורה ב-NodeJF, שזה מאוד מאוד דומה. עכשיו, אחרי שהקוד הזה הסתיים לרוץ בנפרד ולא הפריע לג'אווה סקריפט הרגיל לרוץ, אז אוטומטית הוא מועבר לאזור השלישי שנקרא Call Back Queue, וממתין כאן עד שג'אווה סקריפט תסיים לעשות
את מה שיש לה לעשות בכל סטאק הרגיל. ברגע ש-JavaScript מסיימת לעשות את מה שיש לה לעשות בכל סטאק הרגיל, נכנס לפעולה החלק הרביעי, שזה העיגול הזה שנקרא Event Loop, והוא לוקח את הקטע קוד שממתין ושם אותו פה לריצה בטרד האחד והיחיד של-JavaScript.
אז בואו נראה כמה הרצות שונות בסימולטור הזה. אז יש לנו פה איזשהו קטע קוד ואנחנו מריצים אותו ואנחנו רואים Operation A, Operation B ו-Operation C, וכל אופרשן יתבצע בצורה נפרדת ופשוט היה צריך להמתין לסיומו עד שהאופרשן הבא יתבצע.
הנה A, הנה B, הנה C, קיבעי קודים של JavaScript. גם אם לדוגמה הייתי מבצע פה איזושהי לולאה שלוקחת הרבה זמן, בואו נבצע פה איזשהו Loop i שווה 1, x קטן שווה ל-5i++ ובתוך הלולה אני מבצע איזשהו קטע קוד.
הסימולטור הזה שאנחנו עובדים איתו בעצם כל פקודה הוא גורר אותה למשך שנייה ולכן כל הלולה הזאת כביכול תיקח חמש שניות. בואו נריץ את זה, הנה Operation A, הנה B, הנה C והנה הוא מתחיל להריץ את ההלולה חמש פעמים וזה אומר שהכל Stack עכשיו עובד, שניות כמובן שבמציאות זה יהיה בשבריר שנייה אבל יכול להיות שבאמת העולה הזאת לוקחת חמש שניות והיא תוקעת לנו את כל האזור הזה של ה call stack עכשיו בואו רגע נחזור לאותו מצב
שהיה לנו מקודם שבו יש לנו את Operation A, B ו-C ו-Operation B לוקח הרבה זמן אבל זה לא איזשהו עיבוד תמונה שאני חייב את החישובים בג'אווה סקריפט אלא בוא נניח גלישה לשרת מרוחק.
איך אנחנו מבצעים גלישה לשרת מרוחק ב-JavaScript? אנחנו עושים את זה על ידי Fetch ואומרים לו פה לאיזה כתובת אני רוצה לגלוש ולבסוף מה אני רוצה לעשות כאשר המידע מוחזר בחזרה.
זאת בעצם שיטה אחת לבצע את הגלישה. אם JavaScript הייתה אומרת לנו, תקשיבו אין בעיה אני גולשת לשרת מרוחק אבל אני ממתינה בדיוק כאן עד שהמידע יוחזר בחזרה, זה היה תוקע לנו את כל הזרימה של התוכנית ו-Operation C לא היה יכול להתבצע עד שהיינו מסתיימים.
עכשיו, בדפדפן מה שיקרה זה שכל הדפדפן ייתקע. היוזר לא יוכל לעבוד עם הדפדפן הזה עד שהפקודה הזאת לא תסתיים אם היא הייתה פקודה סינכרונית.
פקודה סינכרונית, הכוונה שאנחנו ממתינים לה. בעצם היוזר היה רואה דפדפן כפו והוא לא יכל לבצע שום דבר עם הדפדפן. לכן ב-JavaScript הדבר הזה לא מתבצע בכל סטאק הרגיל, אלא מתבצע ב-Web APIs.
לצערי הסימולטור הזה לא תומך בפקודת Fetch, הוא לא יכול להראות את ה-Fetch כאן, אבל הוא כן תומך בפקודות אחרות שאנחנו כן יכולים לראות אותן כאן בצד. בואו רגע ניקח את Operation B ונראה ופקודה נוספת של set timeout.
set timeout זאת פקודה שאומרת, תנו לי איזושהי פונקציה, בואו ניתן איזושהי פונקציה כזאת, האמת בואו ניתן לה שם, אז ניקח אותה כפונקציה רגילה, נקרא לה timeout, ומה שאנחנו רוצים לעשות זה להגיד, אוקיי, הפונקציה הזאת לוקח לה להתבצע 5 שניות, או בעוד 5 שניות תבצע אותה,
את הפונקציית timeout הזאת. ורק אז Operation B באמת תתבצע. אז הנה Operation B נמצא כרגע. זאת אומרת שבג'אווה סקריפט יש לנו שירות שהוא חלק מה-Web APIs של הדפדפן שנקרא Set Timeout.
ג'אווה סקריפט מאפשרת לקרוא לפונקציה Set Timeout, והפונקציה Set Timeout בעצם לוקחת את הקטע קוד שיש כאן ומזיזה אותו הצידה ל-Web APIs, ורק בעוד המילי שניות שכתבנו כאן, היא תיקח את הקטע קוד הזה ותשים אותו ב-callback queue.
היא לא יכולה להחזיר אותו מיידית ל-call stack, כי יכול להיות שה-call stack כרגע נמצא בפעילות ומבצע איזושהי פעולה.
עכשיו נכנס לפעולה ה-event loop ואומר, רגע, רגע, בואו נאזין מתי יש לי פה אפשרות להכניס כאן קטע קוד לביצוע, וברגע שזה מסתיים, הופה, הקטע קוד הזה נכנס ומתבצע.
בואו נראה מה בעצם זה עושה. אז נריץ את זה, נראה Operation A, כל ה-Set Timeout נמצא פה, הנה Operation C התבצע, האמת שהוא היה אמור להסתיים ולהיעלם, אבל זה איזשהו באג בסימולטור הזה.
אולי ננסה לרפרש הכל ולראות אם הוא יטפל בבאג. Operation A, כל ה-Set Timeout, Operation C, יופי, Operation C הסתיים. ברגע שה-timeout נגמר, הוא הגיע לכאן, ושימו לב מה קרה, לא היה שום דבר בתוך ה-call stack, אז ה-event loop, הופה, נתן ל-call stack אפשרות לבצע את זה.
עכשיו, מה קורה אם, בואו נניח ניקח כאן מצב של שתי שניות, אבל יש לנו כאן מספר דברים שאנחנו מבצעים, זה Operation C, Operation D, Operation E, בואו נשים את Operation F, בסדר?
A, B, C, D, E, F, כאשר Operation B נתן לו רק שתי שניות, זאת אומרת שאחרי שתי שניות הוא יעבור ל-callback Q, ועדיין יש פה דברים שרצים.
אני מזכיר לכם, האמולטור הזה כל פקודה מריץ במשך שנייה, ולכן מה שהאמולטור יעשה הוא יריץ את זה ארבע שניות, זאת אומרת ששתי שניות הקוד הזה יצטרך להמתין ב-callback Q. בואו נריץ, והנה יש לנו את Operation A, Operation B נכנס שתי שניות ומיד עובר לכאן, אבל אנחנו עדיין לא יכולים לתת לו לפעול, כי יש פה פעולה, ורק כשהפעולות מסתיימות,
האבנט לופ נתן לו אפשרות להתבצע. כמובן שיכול להיות מצב שיש לנו סדרה של דברים שנמצאים בתוך ה-Web APIs. בואו ניקח דוגמה שזה לוקח חמש שניות, זה לוקח שלוש שניות ובואו ניקח עוד אחד שנמצא שם שנייה.
זה Operation B1, B2, B3, כל אלה ימתינו בעצם יצאו לביצוע ב-Web API וימתינו בסופו של דבר בכל ב-Q. בואו נריץ את זה.
הינה Operation A, B1, B2 ו-B3 ממתינים כאן והנה אנחנו רואים שהם פשוט ממתינים בכל ב-Q, עד שהמערכת מזהה שאין יותר דברים לביצוע ב-callback ואז מוציאה אותם לפועל.
שוב, אנחנו רואים פה את הבאג באמולטור הזה שמשאיר כאן כל מיני דברים. בואו רגע ננסה עוד פעם לרפרש ולהריץ. אולי עכשיו זה יהיה קצת יותר ברור לו. הוא עדיין משאיר כאן כל מיני דברים.
טוב, זה כבר באג באמולטור עצמו, אנחנו נצטרך להאמין שככה JavaScript פועלת. בסדר, אז מה בעצם קורה ב-Node.js? ב-Node.js יש לנו callback queue,