Hisoka
Hisoka

Reputation: 111

Async/Await in javascript for loop

I have a react component that runs this function on the mounting of the component.

 function getListOfItems(){
    let result = [];
    for(let i=0 ; i<5 ; i++){
        /**
         * code to assign values to some variables namely param1,param2
         */
        getDetails(param1,param2);
    }
    const getDetails = async (param1,param2) => {
        let list = await getAPIresults(param1)
        result.push(list);
        if(result.length === 5){
            //code to update a hook which causes render and displays the text in results array
        }
    }
 }
  
 useEffect(() => {
   getListOfItems()
 },[])

So the code is running but the results array has data in random order. For instance, the results array might look something like this [2,5,1,3,4] where I expect it to be like this [1,2,3,4,5] This means the above code is not running async tasks in the order of their arrival. So could someone help me out to fix this, I want the code to make async requests in order of their arrival.

Upvotes: 2

Views: 1292

Answers (3)

Ethan Lipkind
Ethan Lipkind

Reputation: 1156

You might want to use Promise.all; this would preserve the order as well:

Promise.all: Order of resolved values

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074138

So the code is running but the results array has data in random order.

That's because your loop calls getDetails repeatedly without waiting for the previous call to complete. So all the calls overlap and race.

If it's okay that they overlap but you need the results in order, use Promise.all and have getDetails return its results (rather than pushing them directly).

If you can't make getListOfItems an async function:

const getDetails = async (param1,param2) => {
    let list = await getAPIresults(param1)
    if(result.length === 5){
        //code to update a hook which causes render and displays the text in results array
    }
    return list;
}
const promises = [];
for (let i = 0; i < 5; ++i) {
    promises.push(getDetails(param1, param2));
}
Promise.all(promises)
.then(results => {
    // `results` is an array of the results, in the same order as the
    // array of promises
})
.catch(error => {
    // Handle/report error
});

If you can (and the caller will handle any error that's propagated to it via rejection of the promise from getListOfItems):

const getDetails = async (param1,param2) => {
    let list = await getAPIresults(param1)
    if(result.length === 5){
        //code to update a hook which causes render and displays the text in results array
    }
    return list;
}
const promises = [];
for (let i = 0; i < 5; ++i) {
    promises.push(getDetails(param1, param2));
}
const results = await Promise.all(promises)
// `results` is an array of the results, in the same order as the
// array of promises

If you need them not to overlap but instead to run one after another, your best bet is to use an async function for the loop.

If you can't make getListOfItems an async function:

const getAllResults = async function() {
    const results = [];
    for (let i = 0; i < 5; ++i) {
        results.push(await getDetails(param1, param2));
    }
    return results;
}
const getDetails = async (param1,param2) => {
    let list = await getAPIresults(param1)
    if(result.length === 5){
        //code to update a hook which causes render and displays the text in results array
    }
    return list;
}
getAllResults()
.then(results => {
    // `results` is an array of the results, in order
})
.catch(error => {
    // Handle/report error
});

If you can (and the caller will handle any error that's propagated to it via rejection of the promise from getListOfItems):

const results = [];
for (let i = 0; i < 5; ++i) {
    results.push(await getDetails(param1, param2));
}
// Use `results
const getDetails = async (param1,param2) => {
    let list = await getAPIresults(param1)
    if(result.length === 5){
        //code to update a hook which causes render and displays the text in results array
    }
    return list;
}

Upvotes: 2

Seth Lutske
Seth Lutske

Reputation: 10676

You need to use the await keyword again to wait for each iteration of the loop to complete before it moves on to the next go-round.

await getDetails(param1,param2)

But as you can only do this in an async function, your getListOfItems will also need to be an async function.

async function getListOfItems(){
    let result = [];
    for(let i=0 ; i<5 ; i++){
        await getDetails(param1,param2);
    }
    const getDetails = async (param1,param2) => {
        let list = await getAPIresults(param1)
        result.push(list);
        if(result.length === 5){}
    }
 }

Upvotes: 4

Related Questions