שגיאה באסינכרוניות (async-await)
-
מדובר בקוד שאמור לתשאל את הAPI של קול חי מיוזיק, ולקבל נתונים על רשימת שידורים חיים נבחרים (אפשר גם לקבל בבת אחת את המידע על כולם, אבל אני העדפתי ככה מסיבות שונות).
// מערך של מספרי שידורים נבחרים let listLive = [35, 2, 3, 5, 37, 7, 16, 15, 23, 8, 27]; /** * קבלת מידע בפועל מהAPI * @returns מערך של נתוני הערוצים הנבחרים */ async function getLiveData() { let arrayLiveInfo = listLive.map(function (indexLive) { let resData = await fetch(`https://kcm.fm/Home/LiveJ/${indexLive}`); let jsonData = await resData.json(); return jsonData; }); return arrayLiveInfo; } console.log(getLiveData());
משום מה זה מחזיר שגיאה:
ואני לא מבין למה, הרי בשורה 7 הצהרתי על הפונקציה כאסינכרונית... -
@צדיק-תמים
עליך לעשות כך/** * קבלת מידע בפועל מהAPI * @returns מערך של נתוני הערוצים הנבחרים */ async function getLiveData() { let arrayLiveInfo = await listLive.map(async function (indexLive) { let resData = await fetch(`https://kcm.fm/Home/LiveJ/${indexLive}`); let jsonData = await resData.json(); return jsonData; }); return arrayLiveInfo; } console.log(getLiveData());
וההסבר הוא שאתה מעביר ל listLive.map פונקציה שתטפל בקריאות, ואת הפונקציה ההיא לא הגדרת כ async.
ההגדרה של async אמורה להיות על הפונקציה עצמה שבתוכה שמים את await, לא מספיק להגדיר בסקופ שמעל. -
@צדיק-תמים אמר בשגיאה באסינכרוניות (async-await):
@חוקר ניסיתי גם את זה, אבל בצורה כזאת זה לא מספק את הנתונים... ניסית להריץ את הקוד שהבאת?
ניסיתי להריץ אבל קיבלתי שגיאה שאין לי מערך
listLive
-
//const fetch = require('node-fetch'); מיועד לריצה באמצעות nodejs let listLive = [35, 2, 3, 5, 37, 7, 16, 15, 23, 8, 27]; async function getLiveData() { let a = []; for (let i in listLive) { let listLiveElement = listLive[i]; let resData = await fetch(`https://kcm.fm/Home/LiveJ/${listLiveElement}`); listLiveElement = await resData.json(); a.push(listLiveElement) } return a; } getLiveData().then(x => { console.log(x) })
אני רואה שרק עם for זה עובד
-
@צדיק-תמים await זה צורה יותר חמודה לעשות promise, אבל בסוף זה promise.
פונקציה של async בעצם מחזירה promise, ולא את הערך הסופי.
בשביל לגשת לערכים אתה צריך לעשות await על כל אחת מהתוצאות, או להשתמש בPromise.all שתודיע לך על סיום של כולם. -
@צדיק-תמים אמר בשגיאה באסינכרוניות (async-await):
@dovid אמר בשגיאה באסינכרוניות (async-await):
בשביל לגשת לערכים אתה צריך לעשות await על כל אחת מהתוצאות
כלומר? הרי בשורה 9 אני ממתין לAPI, ובשורה 10 אני ממתין לתשובה המלאה מהשרת...
ממתין בתוך הפונקציה, הבעיה שכל פונקציה מסוג async מחזירה מיידית ערך של פרומיס לפני כל ההמתנות. ולכן מי שקורא לה צריך גם הוא לשים await קרי: המתן לכל ההמתנות שבתוך הפונקציה.
-
@dovid אמר בשגיאה באסינכרוניות (async-await):
או להשתמש בPromise.all שתודיע לך על סיום של כולם.
הסתכלתי עכשיו על הPromise.all כאן ואני רואה שצריך להעביר לזה מערך של ההבטחות.
איך אני מעביר? הרי הסקופ של הלולאה (שבתוכה ההבטחה עצמה) מתפוגג בכל ריצה, והסקופ של הפונקציה גם כן לא גלוי כלפי חוץ... -
@צדיק-תמים לא אמרתי שלא עובד עם map. אמרתי שאתה חייב או לחכות לכל תוצאה, או לעשות Promise.all שממתין עבורך לכל תוצאה.
תריץ את הקוד הבא בקונסול
הוא ממחיש את התוצאה ההמיידית של פונקציה אסינכרונית, תתעלם מgetNumber שרק מחליפה את הגישה לfetch, ותראה מה הפלט שלהfunction getNumber(value){ return new Promise((y,x) => setTimeout(_ => y(value * value), Math.random() * 4000)); } let listLive = [35, 2, 3, 5, 37, 7, 16, 15, 23, 8, 27]; function getNumbersList(){ return listLive.map(async function(item){ console.log("כעת אההממ רוצה להתחיל את עיבוד " + item); let result = await getNumber(item); console.log("כעת גמר עיבוד של " + item); return result; }); } let results = getNumbersList(); for (let index = 0; index < results.length; index++) console.log(`תוצאה מס ${index}: ${results[index]}`); for (let index = 0; index < results.length; index++) console.log(`תוצאת המתנה מס ${index}: ${await results[index]} `);
אתה תראה שראשית כל מודפסים כל ה"כעת אההממ רוצה להתחיל את עיבוד", ואז כבר הלולאה מגיעה לעבור על איברי המערך למרות שהם לא סיימו את פעולתם.
בקוד שלך השגיאה היחידה הייתה חוסר הasync בפונקציה של הmap כמו ש@חוקר תיקן אותך בהודעתו הראשונה
אלא שהconsole.log(getLiveData());
ידפיס מערך של [object Promise] כי א) הוא קורה לפני שהם סיימו את העיבוד, ב) הוא מקבל מערך של פרומיסים ולא מערך של תוצאותיהם.
שינוי השורה האחרונה לזה:for(let y of getLiveData()) console.log(await y);
יעשה את העבודה.
-
@צדיק-תמים אמר בשגיאה באסינכרוניות (async-await):
@dovid אמר בשגיאה באסינכרוניות (async-await):
או להשתמש בPromise.all שתודיע לך על סיום של כולם.
הסתכלתי עכשיו על הPromise.all כאן ואני רואה שצריך להעביר לזה מערך של ההבטחות.
איך אני מעביר? הרי הסקופ של הלולאה (שבתוכה ההבטחה עצמה) מתפוגג בכל ריצה, והסקופ של הפונקציה גם כן לא גלוי כלפי חוץ...זה מה שעד עכשיו לא הבנת, הgetLiveData() מחזיר מערך של הבטחות, כי הmap מתרגם כל מספר להבטחה על תוצאת אינטרנט.