לקרוא ל-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 כוכבים

 

 

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

 

= 3 + 9