פעולה סינכרונית nodejs
-
@yossiz אמר בפעולה סינכרונית nodejs:
@חוקר
וואוו, קוד כל כך ארוך... (ניתן לזקק את הקוד עד שתקבל דוגמה מינימלית שימחיש את הבעיה)עוד לא קראתי את הכל בעיון, אבל מיד אני רואה שה-then בשורה 44 לא אמור להיות מקונן בתוך הפונקציה, אלא משורשר, כי בדרך שאתה עושה, אתה מחזיר ערך לפני שהפרומייז ש-
ftp.list
מחזיר נהיה resolved. תחליף את שורה 42-44 לזה:ftp.connect({host: '31.168.172.22', user: 'user', password: 'pass'}) .then(()=> ftp.list('/limudklali/' + HDate.getFullYear() + '/' + Month_arr[HDate.getMonth()] + '/' )) .then(function (file) { ....
אשמח לקצת יותר הסבר, כי אני לא הצלחתי לרדת לעומק הנושא של פורמיז.
הרי חוץ מהבדיקה הראשונה בשורה 44 יש לי את כל שאר הלולאה של הבדיקה בהמשך.
עלי לעשות כעת כל שלב ב then מה ftp.connect הראשון?
וכן אשמח להבין מדוע אם זה בתוך פונצקיה זה יחזיר מיד ערך, משא"כ אם זה לא בתוך פונקציה?
תודה -
סליחה לא הייתי מספיק ברור
@חוקר אמר בפעולה סינכרונית nodejs:
עלי לעשות כעת כל שלב ב then מה ftp.connect הראשון?
לא,
משתמשים בפרומיס כאשר צריך לעשות פעולה אחרי שדבר עתידי יקרה, אבל לא רוצים להשבית את ה-thread עד שזה יקרה, אז במקום לשבת ולחכות בחוסר מעש עד שהערך הרצוי מתקבל, עושים אובייקט שנקרא פרומיס, ומדביקים פונקציה ב-then שלו, וההתנהגות של האובייקט הזאת הוא שכאשר שערך מתקבל הוא מריץ את הפונקציה.
משרשרים פרומיסים, כאשר אחרי שמתקבל הערך הראשוני, (במקרה שלך - חיבור ה-FTP) צריך לחכות לעוד ערך עתידי. (במקרה שלך - רשימת קבצים).
אתה לא יכול להריץ את הקוד שלך מיד אחרי שאתה קורא ftp.connect, כי החיבור עדיין לא קיים, יש לך רק פרומיס של חיבור עתידי, אז אתה מדביק קוד ל-then של הפרומיס של החיבור שירוץ כאשר החיבור קיים, אבל זה שהחיבור קיים רק אומר שיש לך חיבור אבל עוד לא קיבלת רשימת הקבצים, אז מייצרים פרומיס שניה שמייצג הערך העתידי השני (דהיינו רשימת הקבצים) ומדביקים ל-then שלו פונקציה שעושה משהו עם רשימת הקבצים (במקרה שלך - מעבד אותו ושולח מייל עם התוצאות).מה שאתה עשית, זה שמיד שהפרומיס הראשון נפתר (ז"א, יש חיבור FTP - בתוך ה-then שלו - ) ייצרת אומנם פרומיס שני (ftp.list), והדבקת ל-then של הפרומיס - קוד שיעבד את הנתונים ושיאכלס את אובייקט strs_tu_mail, אבל מיד עברת לשורה הבאה שמחזיר strs_tu_mail לפני שהוא מאוכלס עם הנתונים.
הפתרון הוא לשלוח את המייל רק מתוך ה-then השני. אפשר לעשות את זה כמו שאתה עשית שקיננת את הפרומיס השני בתוך הראשון, אבל יותר קריא לשרשר אותם (כך נראה לי, בפרט כאשר מדובר בשרשור ארוך),
function doStuff(data) { process(data); mail(data); } ftp.connect(args).then(()=> ftp.list(args) ).then( doStuff )
במקום:
ftp.connect(args).then( ()=>ftp.list(args).then( doStuff; ) )
שתי הדרכים הנ"ל אותו דבר, אבל מה שאתה עשית הוא:
var result = {}; ftp.connect(args).then(()=> { ftp.list(args).then((data)=> process(data, result); }) )).then( email(result/*!!data isn't yet processed!!*/); )
מקווה שזה עוזר...
(רואים שאין לי מושג איך מקובל לפרמט קוד כזה, הא? ) -
@yossiz אמר בפעולה סינכרונית nodejs:
אפשר לעשות את זה כמו שאתה עשית שקיננת את הפרומיס השני בתוך הראשון, אבל יותר קריא לשרשר אותם (כך נראה לי, בפרט כאשר מדובר בשרשור ארוך),
יש גם הבדל בין שתי הדרכים אם כותבים catch בסוף, שכאשר מקוננים זה יתפוס רק שגיאות של הפרומיס הראשון, אבל אם משרשרים זה יתפוס כל השגיאות בשרשרת.
-
@yossiz אמר בפעולה סינכרונית nodejs:
ftp.connect(args).then(()=>
ftp.list(args).then((data)=>
process(data);
)
)).then(
email(/!!data isn't yet processed!!/);
)לא דייקת, כי ככה גם יעבוד טוב! כי עשית שורה בודדת שזה מתודה מקוצרת וכאילו עשית return שזה מחזיר את הפרומייז השני של הthen וממילא הthen הבא ימתין לסיומו.
אלא שהוא לא עשה מתודה מקוצרת אלא בלוק פקודות וreturn הוא על ערך מיידי שחוזר תיכף.בכל אופן אני חושב שכדאי שחוקר יתמקד טיפה בפרומייז בצורה טהורה, למרות ההרגל שלו ללמוד רק תוך כדי יישום.
-
למעשה מה שלא הצלחתי להבין.
אני צריך לעשות לולאה של בדיקות ע"י FTP (אדרבה שכל הבדיקה תבוצע בבת אחת), ומצד שני בגמר הרצת הלולאה יבוצע העיבוד והשליחה למייל.
לא כ"כ נראה לי ישים לעשות לולאה שיוצרת thenים מה ftp.connect הראשון.
או אולי יש כן איזה דרך, וא"כ זה נראה לי הכי פשוט, אך עדיין לא מושלם כי אז תבוצע כל בדיקה בלולאה בנפרד, וחבל על הזמן.
א"כ אני כן צריך משהו שמריץ הכל ביחד ובסיום אני מקבל את הנתונים.
אולי בעצם ניתן לעשות זאת עם Promise.all()?
תודה -
@חוקר
חוששני שאתה לא בקטע כעת של "להבין" אלא בקטע של "להצליח", אני טועה?
הסיבה שלא כדאי לעשות thenים בלולאה היא כיון שזה בזבוז זמן: אתה לא צריך שכל בדיקה תחכה לאחת, מה אכפת לך שהם יבוצעו במקביל? מה שחשוב לך זה לדעת מתי נגמרו כל הבדיקות ומה התוצאות שלהם. Promise.all/Promise.race הם בדיוק בשביל זה. -
@dovid אמר בפעולה סינכרונית nodejs:
@חוקר
חוששני שאתה לא בקטע כעת של "להבין" אלא בקטע של "להצליח", אני טועה?
הסיבה שלא כדאי לעשות thenים בלולאה היא כיון שזה בזבוז זמן: אתה לא צריך שכל בדיקה תחכה לאחת, מה אכפת לך שהם יבוצעו במקביל? מה שחשוב לך זה לדעת מתי נגמרו כל הבדיקות ומה התוצאות שלהם. Promise.all/Promise.race הם בדיוק בשביל זה.נראה לי שציינתי את זה שעדיף לא לעשות כך בגלל הבעיה הזו.
בהחלט אני חושב איך ניתן להגיע להצלחה, אבל אני מנסה במקביל ללמוד את פרומיס
(אני מסתפק האם לבצע אחרי הלמידה בצורה א-סינכרונית, או תוך כדי, בצורה סינכרונית..) -
ייתכן שאני על הכיון הנכון.
הקוד הזה עבד לי טובvar promises = []; for (let i =0;i<3;i++) { console.log('files'+i); promises.push(ftp.list('/limudklali/' + HDate.getFullYear() + '/' + Month_arr[HDate.getMonth()] + '/')); } Promise.all(promises).then(function(values) { ftp.end(); console.log(values); });
והפלט היה:
files0 files1 files2 [ [ { type: '-', name: '20190506.wav', target: undefined, sticky: false, rights: [Object], acl: false, owner: 'ftp', group: 'ftp', size: 680204, date: 2019-05-05T21:08:00.000Z }, { type: '-', name: '20190507.wav', target: undefined, sticky: false, rights: [Object], acl: false, owner: 'ftp', group: 'ftp', size: 749004, date: 2019-05-07T05:17:00.000Z }], [ { type: '-', name: '20190506.wav', target: undefined, sticky: false, rights: [Object], acl: false, owner: 'ftp', group: 'ftp', size: 680204, date: 2019-05-05T21:08:00.000Z }, { type: '-', name: '20190507.wav', target: undefined, sticky: false, rights: [Object], acl: false, owner: 'ftp', group: 'ftp', size: 749004, date: 2019-05-07T05:17:00.000Z }], [ { type: '-', name: '20190506.wav', target: undefined, sticky: false, rights: [Object], acl: false, owner: 'ftp', group: 'ftp', size: 680204, date: 2019-05-05T21:08:00.000Z }, { type: '-', name: '20190507.wav', target: undefined, sticky: false, rights: [Object], acl: false, owner: 'ftp', group: 'ftp', size: 749004, date: 2019-05-07T05:17:00.000Z }]]
שזה אומר שמצד אחד הלולאה רצה מיידית על הכל, ומצד שני רק בסיום הלולאה יש לי את התוצאות.
השאלה היא א"כ מה הכי פרקטי שמצד אחד יש לי את הנתונים לביצוע העיבוד, ומצד שני יש לי מערך של כל הפרומיסים עבור ה Promise.all.
האם יש משהו בJS הצבת משתנה ששמו דינאמי, כמו בPHP למשל:${'num' . $i} = $ftp->list('/limudklali/' + HDate.getFullYear() + '/' + Month_arr[HDate.getMonth()] + '/'); print ${'num' . $i};
בהדפסת משתנה בתוך מחרוזת ראיתי שזה קיים גם בJS
res.end(` my num is: ${'num' + i}, good by`);
אבל לא הצלחתי לעשות הצבת משתנה בסגנון זה.
תודה -
@חוקר אמר בפעולה סינכרונית nodejs:
השאלה היא א"כ מה הכי פרקטי שמצד אחד יש לי את הנתונים לביצוע העיבוד, ומצד שני יש לי מערך של כל הפרומיסים עבור ה Promise.all.
האם יש משהו בJS הצבת משתנה ששמו דינאמי, כמו בPHP למשל:לא הבנתי את המשפטים והקשר ביניהם.
-
-
@dovid אמר בפעולה סינכרונית nodejs:
@חוקר אמר בפעולה סינכרונית nodejs:
השאלה היא א"כ מה הכי פרקטי שמצד אחד יש לי את הנתונים לביצוע העיבוד, ומצד שני יש לי מערך של כל הפרומיסים עבור ה Promise.all.
האם יש משהו בJS הצבת משתנה ששמו דינאמי, כמו בPHP למשל:לא הבנתי את המשפטים והקשר ביניהם.
אני חשבתי על כיון של הצבת משתנה עבור כל לולאה נניח בשם
'num' + i
ובמקביל ליצור מערך שדוחפים אליו את השמות הדינאמים.
ואז להעביר לPromise.all את המערך של שמות המשתנים שעליו להמתין עד שיגמרו את פעילותם. -
זה גם הלך לי.
האם יש רעיון יותר טובvar promises = []; for (let i =0;i<3;i++) { console.log('files'+i); global['files'+i] = (ftp.list('/limudklali/' + HDate.getFullYear() + '/' + Month_arr[HDate.getMonth()] + '/')); promises.push(global['files'+i]); } Promise.all(promises).then(function(values) { ftp.end(); console.log(values); });
כאן הלולאה עובדת מצויין, הכל מבוצע במקביל, ומצד שני יש לי Promise.all בסיום
-
@avr416 אמר בפעולה סינכרונית nodejs:
@חוקר מה באת להרויח כאן?
בשני המקרים אתה מחזיק מערך של כל הפרומיסים, ואז קורא לו בפונקציה promise.all.
אלא שעכשיו גם הוספת את זה למערך גלובאלי נוסף.. בשביל מה?כדי שיהיה לי גישה אח"כ לעיבוד הנתונים.
אני צריך אפשרות לעבד כל אחת מהתוצאות.
את העיבוד אני יעשה בתוך ה promise.all.
בראשון הכל נכנס לתוך מערך בלי שיהיה לי את השם של התוצאה ואז אין לי איך לעבד את התוצאה לכל אחד, לדעת של מי כל תוצאה. -
@avr416
גלובל הוא הפיתרון למה ששאלתי כאן למעלה https://tchumim.com/post/61118
הוא מערך של כל המשתנים vars בסקופ.
אבל אכן חזרתי בי מהצורך להשים אותם כמשתנה גלובאלי, אלא ליצור אובייקט נפרד לקבלת התוצאות, ואז ליצור משהו בסגנון שציינת עבור ה Promise.all -
@avr416 אמר בפעולה סינכרונית nodejs:
@חוקר
אם global הוא אובייקט, אתה יכול להמיר אותו למערך ולהעביר אותו ישירות לפרומיס.all וא"צ ליצור עוד מערך של פרומיסס.Promise.all(Object.keys(global).map(k=>global[k]))
זה אמור להחזיר לך מערך של כל הפרומיסים שדחפת לגלובל
בכל מקרה, אני חוזר בי..
הדרך שלך עדיפה מבחינת ביצועים על ההצעה שלי. כי אתה עשית הכל בלולאה אחת, ואני הוספתי לך כאן עוד כמה (מאחורי הקלעים..) -
@חוקר אם אתה רוצה בתוצאת הפרומיס נתון נוסף ממה שהוא מחזיר במקור, אתה יכול להוסיף את הנתון בthen שבעצם יוצר פרומייז נוסף בהסתיים הראשון:
var promises = []; for (let i =0;i<3;i++) { console.log('files'+i); var pr = ftp.list($`/limudklali/${HDate.getFullYear()}/${Month_arr[HDate.getMonth()]}/`) .then(x => { return {source: i, result: x} })); promises.push(pr); } Promise.all(promises).then(function(values) { ftp.end(); console.log(values); });