javascript - מה ההבדל בין תכנות סינכרוני ואסינכרוני (ב-node.js)
קראתי את nodebeginner ונתקלתי בשני קטעי הקוד הבאים.
האחד הראשון:
var result = database.query("SELECT * FROM hugetable");
console.log("Hello World");
השני:
database.query("SELECT * FROM hugetable", function(rows) {
var result = rows;
});
console.log("Hello World");
אני מבין מה הם אמורים לעשות, הם מבצעים שאילתה במסד הנתונים כדי לקבל את התשובה לשאילתה. ואז console.log('Hello world').
הראשון הוא לכאורה קוד סינכרוני. והשני הוא קוד אסינכרוני.
ההבדל בין שני החלקים מאוד מעורפל לי. מה תהיה התוצאה?
חיפוש בגוגל על תכנות אסינכרוני גם לא עזר לי.
תשובה #1
ההבדל הוא שבדוגמה הראשונה, התוכנית תחסום בשורה הראשונה. השורה הבאה (console.log) תצטרך לחכות.
ב-הדוגמה השנייה, ה-console.log יתבצע בזמן שהשאילתה מעובדת. כלומר, השאילתה תעובד ברקע, בזמן שהתוכנית שלך מבצעת דברים אחרים, וכאשר נתוני השאילתה יהיו מוכנים, תוכל לעשות איתם כל מה שתרצה.
אז, בקיצור: הדוגמה הראשונה תחסום, בעוד שהשנייה לא.
הפלט של שני הדוגמאות הבאות:
// Example 1 - Synchronous (blocks)
var result = database.query("SELECT * FROM hugetable");
console.log("Query finished");
console.log("Next line");
// Example 2 - Asynchronous (doesn't block)
database.query("SELECT * FROM hugetable", function(result) {
console.log("Query finished");
});
console.log("Next line");
יהיה:
השאילתה הושלמה
שורה הבאהשורה הבאה
השאילתה הסתיימה
הערה
בעוד ש-Node עצמו הוא חד־תהליכי, ישנם כמה משימות שיכולות לרוץ במקביל. לדוגמה, פעולות של מערכת הקבצים מתבצעות בתהליך שונה.
זו הסיבה ש-Node יכול לבצע פעולות אסינכרוניות: חוט אחד מבצע פעולות של מערכת הקבצים, בזמן שחוט ה-Node הראשי ממשיך להפעיל את קוד ה-JavaScript שלך. בשרת מונחה אירועים כמו Node, חוט מערכת הקבצים מודיע לחוט ה-Node הראשי על אירועים מסוימים כמו סיום, כישלון או התקדמות, יחד עם כל נתון הקשור לאירוע הזה (כגון תוצאת שאילתה למסד נתונים או הודעת שגיאה) והחוט הראשי של Node מחליט מה לעשות עם הנתונים האלה.
אתה יכול לקרוא עוד על זה כאן: איך מודל ה-IO הלא חוסם בעל חוט יחיד עובד ב-Node.js
תשובה #2
ההבדל בין שתי הגישות האלה הוא כדלקמן:
אופן סינכרוני: הוא מחכה שכל פעולה תסתיים, ורק לאחר מכן מבצע את הפעולה הבאה. לגבי השאילתה שלך: הפקודה console.log() לא תבוצע עד ש-והשאלה תסיים להתבצע כדי לקבל את כל התוצאות מהמסד נתונים.
דרך אסינכרונית: היא אף פעם לא מחכה לכל פעולה להסתיים, אלא מבצעת את כל הפעולות בפעם הראשונה בלבד. התוצאה של כל פעולה תטופל ברגע שהתוצאה זמינה. עבור השאילתה שלך: הפקודה console.log() תתבצע מיד אחרי המתודה Database.Query(). בזמן שהשאילתה למסד הנתונים רצה ברקע ומטעינה את התוצאה ברגע שהיא מסיימת לשלוף את הנתונים.
מקרי שימוש
אם הפעולות שלך לא עושות עבודה כבדה מאוד כמו שאילתת נתונים עצומים מהבסיס נתונים אז המשך בצורה סינכרונית, אחרת בצורה אסינכרונית.
בדרך אסינכרונית אתה יכול להציג למשתמש אינדיקטור התקדמות בזמן שאתה ברקע ממשיך בעבודות הכבדים שלך. זהו תרחיש אידיאלי לאפליקציות GUI.
תשובה #3
זה יהפוך למעט ברור יותר אם תוסיף שורה לשני הדוגמאות:
var result = database.query("SELECT * FROM hugetable");
console.log(result.length);
console.log("Hello World");
השני:
database.query("SELECT * FROM hugetable", function(rows) {
var result = rows;
console.log(result.length);
});
console.log("Hello World");
נסה להריץ את אלה, ותשים לב שהדוגמה הראשונה (סינכרונית), result.length יודפס לפני השורה 'Hello World'. בדוגמה השנייה (אסינכרונית), result.length (ככל הנראה) יודפס אחרי השורה 'Hello World'.
זה בגלל שבדוגמה השנייה, ה-database.query מתבצע באופן אסינכרוני ברקע, והסקריפט ממשיך מיד עם "Hello World". ה-console.log(result.length) מתבצע רק כאשר השאילתה למסד הנתונים הושלמה.
תשובה #4
קודם כל, אני מבין שאני מאוחר במענה על השאלה הזו.
לפני שנדון בסינכרוני ואסינכרוני, בואו נסתכל בקצרה על איך תוכניות רצות.
במקרה הסינכרוני, כל פקודה מסתיימת לפני שהפקודה הבאה מתבצעת. במקרה זה התוכנית מוערכת בדיוק לפי סדר הפקודות.
ככה אסינכרוני עובד ב-JavaScript. יש שני חלקים במנוע ה-JavaScript, חלק אחד שבודק את הקוד ומכניס פעולות לתור וחלק אחר שמעבד את התור. עיבוד התור קורה בשרשור אחד, ולכן רק פעולה אחת יכולה לקרות בכל פעם.
כאשר נראית פעולה אסינכרונית (כמו השאילתא השנייה למסד הנתונים), הקוד מנותח והפעולה מונחת בתור, אך במקרה זה נקבע קריאה חוזרת שתתבצע כאשר הפעולה תושלם. ייתכן שבתור כבר ישנן פעולות רבות. הפעולה שמול התור מעובדת ומצוינת מהתור. ברגע שהפעולה עבור שאילתת מסד הנתונים מעובדת, הבקשה נשלחת למסד הנתונים וכאשר תסתיים הקריאה החוזרת תתבצע בסיום. בזמן זה, מעבד התור, לאחר ש"טיפל" בפעולה, עובר לפעולה הבאה - במקרה זה
console.log("Hello World");
שאילתת מסד הנתונים עדיין מתבצעת, אך הפעולה console.log נמצאת בראש התור ומבוצעת. מאחר שזו פעולה סינכרונית, היא מתבצעת מיד ולכן מופיעה התוצאה "Hello World". זמן מה לאחר מכן, הפעולה במסד הנתונים מסתיימת, ורק אז הקריאה חזרה (callback) שנרשמה עם השאילתה נקראת ומבוצעת, ומגדירה את הערך של המשתנה result ל-rows.
יתכן כי פעולה אסינכרונית אחת תביא לפעולה אסינכרונית אחרת, פעולה זו השנייה תוצב בתור וכאשר היא תגיע לחזית התור היא תעובד. קריאה לפונקציית החזרה (callback) שנרשמה עם פעולה אסינכרונית היא האופן שבו סביבת הריצה של JavaScript מחזירה את תוצאת הפעולה כאשר היא מסתיימת.
שיטה פשוטה לדעת איזו פעולה ב-JavaScript היא אסינכרונית היא לשים לב אם היא דורשת callback - ה-callback הוא הקוד שיבוצע כאשר הפעולה הראשונה תושלם. בשני הדוגמאות בשאלה, אנחנו יכולים לראות שרק במצב השני יש callback, ולכן הוא הפעולה האסינכרונית משניים. זה לא תמיד כך בגלל הסגנונות השונים של טיפול בתוצאת פעולה אסינכרונית.
כדי ללמוד עוד, קראו על הבטחות. הבטחות הן דרך נוספת שבה ניתן להתמודד עם התוצאה של פעולה אסינכרונית. הדבר הנעים בהבטחות הוא שסגנון הקידוד מרגיש יותר כמו קוד סינכרוני.
ספריות רבות כמו Node 'fs', מספקות גם סגנונות סינכרוניים וגם א-סינכרוניים עבור חלק מהפעולות. במקרים בהם הפעולה אינה אורכת זמן רב ואינה בשימוש תדיר - כמו במקרה של קריאת קובץ תצורה - פעולת סגנון סינכרוני תוביל לקוד שקל יותר לקרוא אותו.
