תחי ישראל - אין לנו ארץ אחרת

תחי ישראל -אין לנו ארץ אחרת

לקרוא ל-API מתוך לולאה של JavaScript ולהספיק לקבל תשובה בזמן

מחבר:
בתאריך:

המדריך נכתב במקור באנגלית: Calling an API from inside a loop with JavaScript

באחד הפרויקטים שלי הייתי צריך לקבל מידע שמקורו ב-API לתוך לולאה. הבעיה נשמעת אולי פשוטה אבל היה לי חשוב לתאם בין המידע שנשלח ומתקבל, בעוד למעשה לולאת ה-JavaScript ששלחה את הקריאות סיימה לרוץ עוד לפני קבלת התגובות. במדריך זה, אני רוצה להסביר מה לא עובד ומה, בסופו של דבר, אפשר לי לתאם בין זמן הקריאה ל-API וקבלת התגובה. אתחיל מהדגמה באמצעות קוד jQuery, שכולם מכירים, ובהמשך אעבור לקידוד מודרני.

לפנות ל-API מתוך לולאת JavaScript

מדריכים קודמים באתר רשתטק הסבירו כבר את הנושא של ביצוע פעולות אסינכרוניות ב-AJAX, ולכן אני לא נכנס לכל הפרטים.

הסקריפט צריך לקרוא 5 פעמים ל-API חיצוני על פי סדר אותו מגדיר המערך הבא:

var tasks = [1,2,3,4,5]
  • ה-API מחזיר את הפרמטר אותו שלחנו אליו. אם שלחנו 1 יחזיר 1, אם 5 - יחזיר 5.

המחשבה הראשונה היא שמשהו פשוט כמו הרצת קריאה ב-POST ל-API מתוך הלולאה תעשה את העבודה:

var tasks = [1,2,3,4,5]

function perform1(x){
    $.post('https://reshetech.co.il/dummy-api/', JSON.stringify({min: x, max: x}), function(res){
        return res.random;
    })
}

for(var x=0; x < tasks.length; x++){
    var task = tasks[x];
    console.log(task);
    
    var res = perform1(task)
    console.log({'task': task, 'result': res})
}

התוצאה:

1
{task: 1, result: undefined}
2
{task: 2, result: undefined}
3
{task: 3, result: undefined}
4
{task: 4, result: undefined}
5
{task: 5, result: undefined}
  • למרות שהמידע נשלח ב-POST, התוצאה לא התקבלה חזרה בזמן ובהתאם הערך של התגובה לא הוגדר (undefined). 
  • הבעיה היא שקריאה ל-API היא א-סינכרונית בעוד הלולאה עובדת באופן סינכרוני מה שגורם לתגובה להגיע אחרי שהלולאה כבר נמצאת במקום אחר.

במקרה של jQuery יש פונקציה מבוססת promise שדוחה את ביצוע הקוד המגיב עד לקבלת המידע חזרה מה-API אז ניסיתי להשתמש בה:

var tasks = [1,2,3,4,5]
    
function perform2(x){
    var d = $.Deferred();
    
    $.post('https://reshetech.co.il/dummy-api/', JSON.stringify({min: x, max: x}), function(res){
        d.resolve(res.random);
    })
    
    return d;
}

for(var x=0; x < tasks.length; x++){
    var task = tasks[x];
    console.log(tasks[x]);
    
    perform2(tasks[x]).done(function(res){
        console.log({'task': task, 'result': res})
    })
}

התוצאה:

1
2
3
4
5
{task: undefined, result: 1}
{task: undefined, result: 3}
{task: undefined, result: 4}
{task: undefined, result: 2}
{task: undefined, result: 5}

נראית כמו צעד בכיוון הנכון כיוון שהסקריפט ידע לחכות לפתרון ה-promise. הבעיה הייתה שהלולאה סיימה לרוץ לפני קבלת תגובת ה-API.

כדי לפתור את הבעיה השתמשתי ב-closure בתוך לולאת forEach במטרה לשמור את ה-state:

var tasks = [1,2,3,4,5]

function perform2(x){
    var d = $.Deferred();
    
    $.post('https://reshetech.co.il/dummy-api/', JSON.stringify({min: x, max: x}), function(res){
        d.resolve(res.random);
    })
    
    return d;
}

tasks.forEach(function(task){
    // console.log(task);
    perform2(task).done(function(res){
        console.log({'task': task, 'result': res})
    })
});

התוצאה:

{task: 4, result: 4}
{task: 1, result: 1}
{task: 3, result: 3}
{task: 2, result: 2}
{task: 5, result: 5}
  • הסקריפט אומנם הצליח לתאם את הפרמטרים הנשלחים ומוחזרים מה-API אבל יש מקום לשיפור. לדוגמה, הסדר של הלולאה לא נשמר וגם סגנון הקוד מיושן למדי.

הדרך המודרנית לבצע את המשימה היא באמצעות Async/Await ופונקצית fetch:

const tasks = [1,2,3,4,5]
    
const api = 'https://reshetech.co.il/dummy-api/';
const perform3 = (x) => {
    return fetch(api, {
        method : 'post',
        body: JSON.stringify({min: x, max: x})
    })
    .then((res) => res.json())
    .catch((error) => console.log(error))
}
  
const managePerformance = async () => {
    try {
        for(let task of tasks){
            // console.log(task)
            const res = await perform3(task);
            console.log({'task': task, 'result': res.random})
        }
    } catch(err) { 
        console.log(err);	    	
    }
}

managePerformance();

התוצאה:

{task: 1, result: 1}
{task: 2, result: 2}
{task: 3, result: 3}
{task: 4, result: 4}
{task: 5, result: 5}
  • התוצאה היא בדיוק מה שצריך. סדר שליחת הנתונים נשמר. בנוסף, הסקריפט כתוב בסגנון מודרני והשימוש בבלוקים של Try/Catch הופך את התהליך לבטוח יותר.

לכל המדריכים בסדרת JavaScript למתקדמים

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים

 

 

המדריכים באתר עוסקים בנושאי תכנות ופיתוח אישי. הקוד שמוצג משמש להדגמה ולצרכי לימוד. התוכן והקוד המוצגים באתר נבדקו בקפידה ונמצאו תקינים. אבל ייתכן ששימוש במערכות שונות, דוגמת דפדפן או מערכת הפעלה שונה ולאור השינויים הטכנולוגיים התכופים בעולם שבו אנו חיים יגרום לתוצאות שונות מהמצופה. בכל מקרה, אין בעל האתר נושא באחריות לכל שיבוש או שימוש לא אחראי בתכנים הלימודיים באתר.

למרות האמור לעיל, ומתוך רצון טוב, אם נתקלת בקשיים ביישום הקוד באתר מפאת מה שנראה לך כשגיאה או כחוסר עקביות נא להשאיר תגובה עם פירוט הבעיה באזור התגובות בתחתית המדריכים. זה יכול לעזור למשתמשים אחרים שנתקלו באותה בעיה ואם אני רואה שהבעיה עקרונית אני עשוי לערוך התאמה במדריך או להסיר אותו כדי להימנע מהטעיית הציבור.

שימו לב! הסקריפטים במדריכים מיועדים למטרות לימוד בלבד. כשאתם עובדים על הפרויקטים שלכם אתם צריכים להשתמש בספריות וסביבות פיתוח מוכחות, מהירות ובטוחות.

המשתמש באתר צריך להיות מודע לכך שאם וכאשר הוא מפתח קוד בשביל פרויקט הוא חייב לשים לב ולהשתמש בסביבת הפיתוח המתאימה ביותר, הבטוחה ביותר, היעילה ביותר וכמובן שהוא צריך לבדוק את הקוד בהיבטים של יעילות ואבטחה. מי אמר שלהיות מפתח זו עבודה קלה ?

השימוש שלך באתר מהווה ראייה להסכמתך עם הכללים והתקנות שנוסחו בהסכם תנאי השימוש.

הוסף תגובה חדשה

 

 

ענה על השאלה הפשוטה הבאה כתנאי להוספת תגובה:

איך קוראים בעברית לצ`ופצ`יק של הקומקום?