במאמר הקודם למדנו על סביבת העבודה של Node.js – איך מתקינים את Node.js על לינוקס או על חלונות. ואיך עובדים על Node.js באמצעות יצירת קובץ js והרצה שלו עם nodejs.
כשאנחנו מדברים על קוד JS, אנחנו מדברים על קוד סינכרוני וקוד אסינכרוני כאשר ב-Node.js אנו משתמשים בקוד אסינכרוני. על מנת להמחיש את כל הסינית הזו, אני רוצה להציג בפניכם קוד קטן של Node.js. הקוד הזה מבצע עבודה מול מערכת הקבצים.
על מנת לבצע את רוב הפעולות ב-node.js אנו קוראים למודולים. המודולים יכולים להגיע ב-core או להטען מבחוץ. אחד המודולים היותר חשובים הוא המודול שעוזר ל-node.js להתעסק עם הקבצים ושמו בישראל הוא fs – יעני file system 🙂 איך אנו קוראים למודולים? באמצעות require – אני ארחיב מאוד על מודולים בהמשך. אבל בוא בינתיים נרגע קצת ונסתכל על הקוד המאוד פשוט הזה:
var fs = require('fs');
filenames = fs.readdirSync(".");
for (i = 0; i < filenames.length; i++) {
console.log(filenames[i]);
}
processId = process.getuid();
console.log(processId);
מה הקוד הזה עושה? לא צריך להיות מהנדס טילים כדי לדעת שמה שהוא עושה זה להדפיס את רשימת הקבצים שיש בתיקיה. בסופו של יום הוא מדפיס את ה-process id. מה הבעיה עם הקוד הזה? אין בעיה בכלל. מה הפלט שלו? על מנת לראות את הפלט שלו אני אשמור אותו כ:test_sync.js ואריץ אותו באמצעות nodejs test_sync.js. מה יצא לי בסוף מזה?
$ nodejs test.js
test.js
test_sync.js
1000
מה אני מקבל פה? את רשימת הקבצים בתיקיה שלי (במקרה הזה test.js וכמובן test_sync.js) ואז מספר שמציין את ה-ProcessId. לא משהו שצריך להפיל אתכם.
מדובר פה בקוד סינכרוני – זה קוד שכל מי שמתכנת JS יותר מיום אמור להכיר. הבעיה היא (או יותר נכון היתרון האדיר של Node.js) שאנו אמורים לכתוב את הקוד כקוד א-סינכרוני. האמת היא שמודול fs הוא אחד המודולים הבודדים שמאפשרים לכתוב קוד סינכרוני. למה אנחנו אמורים לכתוב קוד אסינכרוני? כי קוד סינכרוני תוקע לנו את המערכת. אם נסתכל על הקוד שלעיל, אנו נראה שיש שם בת'כלס שתי פעולות – הדפסת הקבצים, שמותנית בקריאה של התיקיה שבה אנו נמצאים והדפסת ה-processId. כאשר אנו כותבים קוד סינכרוני אנחנו בעצם תוקעים את המערכת ולא מאפשרים לשאר הפעולות להעשות. התקיעה היא לא בהדפסה של שמות הקבצים אלא בהמתנה הארוכה ל-fs כדי שיקרא את רשימת הקבצים.
הרבה יותר חכם לעשות משהו בסגנון הזה – נבצע קריאה ל-fs כדי שיקרא את רשימת הקבצים – עד שהקריאה תושלם, נמשיך במורד הסקריפט. ברגע שהקריאה מושלמת, נוכל להשלים את כל הפעולות שכרוכות בקריאה הזו.
איך עושים את זה? באמצעות callbacks. זה סינטקס שאמור להיות מוכר מאוד לכל מי שהתעסק ב-jQuery בכלל וב-AJAX בפרט. מה שאנחנו עושים זה לקרוא ל-fs ואז לומר – 'מר בחור, עד שתסיים עם הקריאה מה-fs ותשיג לי את כל המידע – תמשיך את הסקריפט ואל תחכה'. איך עושים את זה?
var fs = require('fs');
fs.readdir(".", function (err, filenames) {
var i;
for (i = 0; i < filenames.length; i++) {
console.log(filenames[i]);
}
});
processId = process.getuid();
console.log(processId);
הדבר הכי חשוב פה הוא להסתכל איך הפעולה של ה-fs.readdir בנויה. אני מעביר לה שני ארגומנטים – הראשון הוא הפרמטר שהיא צריכה (באיזה נתיב לחפש) – בדיוק כמו הקוד הסינכרוני. השני הוא פונקציה אנונימית חביבה שמקבלת שני ארגומנטים גם היא: err (לתקלות) ו-filenames) שזו התוצאה. הפונקציה האנונימית מכילה את ההדפסה של שמות הקבצים. את ההדפסה של ProcessId אני מבצע כרגיל.
איך הפלט יראה לפי דעתכם? אותו הדבר?
בוודאי שלא! הוא יראה ככה:
$ nodejs test.js
1000
test.js
test_sync.js
מה השינוי לעומת הפלט של הקוד הסינכרוני? שההדפסה של ה-ProcessId מתבצעת קודם! למה? איך זה יכול להיות? הרי ההדפסה הארורה מופיעה במורד הקוד! ההסבר הוא שכפי שהסברתי קודם לכן – מדובר בקוד א-סינכרוני. ה-callback של fs מופעל רק כשה-fs מסיימת לקרוא את מערכת הקבצים – עד אז הקוד ממשיך כרגיל ואין לנו תקיעות של המערכת.
אם זה נראה לכם מסובך או לא מובן מספיק, הייתי מציע לכם לבדוק את jQuery ו-AJAX – שם בדיוק זה אותו הדבר. אנו מבצעים פעולה (GET באמצעות AJAX לצורך העניין) ומבצעים callback רק כשהתוצאה מגיעה מהשרת, לא לפני. הסינטקס הוא אותו סינטקס.
המהפך מחשיבה של קוד סינכרוני לקוד אסינכרוני היא לא פשוטה, אנחנו נמשיך לדבר על קוד אסינכרוני בהמשך. מדובר באחד הדברים החשובים ביותר ב-Node.js.
כמה הערות חשובות לפני שממשיכים:
קולבקים הם לא הדרך היחידה לכתוב קוד א-סינכרוני בג'אווהסקריפט. יש לנו פרומיסים ויש לנו async await. בסדרת המדריכים הזו אני משתמש בקולבקים בלבד. אבל בספרים שלי "ללמוד ג'אווהסקריפט בעברית" וספר ההמשך "ללמוד Node.js בעברית" אני מלמד על שתי השיטות שלעיל. אם הספרים האלו מעניינים אתכם ואתם רוצים ללמוד ברצינות – נסו אותם.
10 תגובות
ג'ים משך אותי ממש להתחלה…:)
בהרצה האה סינכרונית אני מקבל את השגיאה הבאה:
process.getuid is not a function
ממה יכול להיות שנובע?
זה בגללי, או יותר נכון – זה בגלל מערכת ההפעלה חלונות שלא אוהבת את processId = process.getuid();
אני אתקן את הדוגמה, אבל אפשר להשתמש ב:
process.hrtime(); במקום. זה אמור לעבוד גם על חלונות.
http://stackoverflow.com/questions/10356814/process-getuid-not-working-on-windows
הבנתי את העקרון של הפוסט,הכתיבה שלך בהירה ומובנת!
אך משום מה זה אני מריץ את הקובץ והוא נותן לי הודעות שגיאה.
ניסיתי להעלות צילום מסך אבל אי אפשר..
אז אשמח אם תוכל לתת לי את המייל שלך כדי שאשלח אליך הודעה, אם זה בסדר מבחינתך..
בעקרון הוא כותב לי כל מיני דברים, אבל נראה לי שהעיקרי שבהם הוא getid is not a function
זה בגללי, או יותר נכון – זה בגלל מערכת ההפעלה חלונות שלא אוהבת את processId = process.getuid();
אני אתקן את הדוגמה, אבל אפשר להשתמש ב:
process.hrtime(); במקום. זה אמור לעבוד גם על חלונות.
מקצועי וקליל, תודה
תודה רבה! 🙂 בסופו של דבר זה לא מדע טילים… 😉
אני בדיוק לומד ממדריך בUDEMY, ומה שהייתי צריף את הסגירת פינה שלך בנושא (בעיקר בעברית שפת האם).
תודה רבה!
ראשית, תודה על כל המדריכים נעזרתי בהם מספר פעמים!
אני רוצה לדייק מעט, וסליחה מראש על הפילסופים..
אפשר להבין במדיך פה שמה שהופך קוד להיות אסינכרוני וגורם למערכת להמשיך בקוד הלאה בלי לחכות לתשובה מהפונקציה זה הקולבק הפרומיס וכו'.
וזה לא נכון למיטב הבנתי, להיפך, הקולבק מאפשר לשרשר פעולה אחרי פונקציה אסינכרונית כך שהיא תתבצע רק אחרי שתחזור תשובה. באופן סינכרוני.
איך הופכים פונקציה לאסינכונית? האמת אין לי מושג, אולי עם Worker?
אתה טועה בהבנת הדברים.
נכון שקולבק גם מאפשר כמו שאמרת לשרשר פונקציה שתפעל לאחר קבלת תשובה מפונקציה אסינכרונית.
אבל… זו בדיוק אסינכרוניות…
שים לב, במקום שהפונקציה תפעל לפי הסדר הסינכרוני שהיא כובה בדף, אתה גרמת לה באמצעות הקולבק לפעול בסדר אחר.
זו בדיוק אסינכרוניות