Dev01
Dev01

Reputation: 14129

How do I make a forEach() with async/await fire synchronously not asynchronously?

I have the following code:

console.log("TEST 1");
accounts.forEach(async function(account) {
    console.log("TEST 2");
    try {
        var data = await getReservations(account);
    } catch(err) {
        res.status(err.error_code).json(err);
    }
    console.log("TEST 3");
});
console.log("TEST 4");

When it's run, the console reads:

TEST 1
TEST 2
TEST 4
getReservations()
TEST 3

But I want it to wait for getReservations() to finish before it continues past the forEach(). I would like it to output:

TEST 1
TEST 2
getReservations()
TEST 3
TEST 4

Any ideas?

Upvotes: 0

Views: 154

Answers (4)

user663031
user663031

Reputation:

To expand on the other answers, if you really want to use forEach, you could write your own version of it, which we will call forEachAsync:

async function forEachAsync(array, fn, thisArg) {
  for (let i = 0; i < array.length; i++) 
    await fn.call(thisArg, array[i], i, array);
}

// Fake version of getReservations--just wait for a while.
function getReservations(account) {
  return new Promise(resolve => setTimeout(() => resolve(account), account));
}

const accounts = [1000, 500, 2000];  

console.log("TEST 1");
forEachAsync(accounts, async function(account) {
    console.log("TEST 2");
    var data = await getReservations(account);
    console.log("TEST 3", data);
})
.then(() => console.log("TEST 4"));

Upvotes: 1

Lai Xue
Lai Xue

Reputation: 1561

I don't believe you can, however, the code below would work.

(async function()
{   
    console.log("TEST 1");

    for (let i = 0; i < accounts.length; ++i)
    {
        let account = accounts[i];

        console.log("TEST 2");

        try
        {
            var data = await getReservations(account);
        }
        catch (err)
        {
            res.status(err.error_code).json(err);
        }

        console.log("TEST 3");
    }

    console.log("TEST 4");
})();

Upvotes: 1

Jaromanda X
Jaromanda X

Reputation: 1

This is one time a regular for loop works better - the code below is wrapped in an IIFE so await can be used

var accounts = [1, 3];
var getReservations = (account) => new Promise(resolve => setTimeout(resolve, 1000, account*2));
(async function() {
    console.log("TEST 1");
    for(var i = 0, l = accounts.length; i < l; i++) {
        let account = accounts[i];
        console.log("TEST 2");
        try {
            var data = await getReservations(account);
        } catch(err) {
            console.error(err);
        }
        console.log("TEST 3");
    };
    console.log("TEST 4", data); // data should be 6
})();

NOTE: anything AFTER the IIFE will not "await"

Upvotes: 2

Matt Way
Matt Way

Reputation: 33141

If you are happy running all your getReservations functions at the same time, you can use Promise.all(). For example:

Promise.all(accounts.map(getReservations))
  .then(() => console.log('all get reservations completed'))

Or, if you need each call to getReservations to be completed in order, you can build a promise chain using reduce(). For example:

accounts.reduce((p, account) => {
  return p.then(() => getReservations(account)
}, Promise.resolve())
  .then(() => console.log('all get reservations completed'))

Upvotes: 1

Related Questions