lmc
lmc

Reputation: 279

How to wait for promises in a for loop to complete before returning the value

I have a for loop in which each iteration runs a promise and adds the index of the promise to the array if the promise is resolved, and in the end, I want to output the resulted array. However, the function keeps returning the array before completing all the promises in the for loop. As a result, I always get an empty returned array.

My code looks like this:

var str_list = [some strings];

let myFunction = () => {
    var array = [];
    for (i = 0; i < length; i++) {
        new Promise((resolve, reject) => {
            if (str_list[i] fulfills some conditions) {
                resolve();
            } else {
                reject(
                    throw new Error())
            }
        }).then(() => {
            array.push(i);
        }).catch((err) => {
            console.log(i, err);
        });
    }
    return array;
}

Is there a good way to wait for the for loop to complete before returning the value?

Upvotes: 3

Views: 4691

Answers (1)

laptou
laptou

Reputation: 6981

var str_list = ["..."];

let myFunction = async () =>
{
    const promises = [];
    for (i = 0; i < length; i++)
    {
        promises.push(new Promise((resolve, reject) =>
        {
            const condition = // ...
            resolve(condition ? str_list[i] : false);
        }));
    }

    return (await Promise.all(promises)).filter(f => f !== false);
}

Try this. Essentially, what it does is that it adds all of the Promises to an array, and then calls Promise.all on them, which executes them in parallel and waits for all of them to finish.

The results are then returned in order, those results being what was passed into the resolve() function in each Promise, and will be either str_list[i] or false. Then, the call to filter() removes all of the entries that are false.

If you care about being concise, then this can be greatly simplified:

var str_list = ["..."];

let myFunction = async () =>
{
    const promises = str_list.map(async (value, index) => condition ? value : false);

    return (await Promise.all(promises)).filter(f => f !== false);
}

This code works the same way as the first snippet; it's just much shorter.

I made the method async because there is no way to return the results of a Promise without awaiting it. If you don't want to use an async method, you can either return a Promise, which you can then wait on with await or .then():

var str_list = ["..."];

let myFunction = () => {
  const promises = [];
  for (i = 0; i < length; i++) {
    promises.push(new Promise((resolve, reject) => {
      const condition = // ...
        resolve(condition ? str_list[i] : false);
    }));
  }

  return Promise.all(promises).then(strs => strs.filter(f => f !== false));
}

// myFunction().then(results => ...)

Or you can use a callback:

var str_list = ["..."];

let myFunction = (callback) =>
{
    const promises = [];
    for (i = 0; i < length; i++)
    {
        promises.push(new Promise((resolve, reject) =>
        {
            const condition = // ...
            resolve(condition ? str_list[i] : false);
        }));
    }

    return Promise.all(promises).then(results => callback(results.filter(f => f !== false));
}

// myFunction(results => ...);

Upvotes: 2

Related Questions