0

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?

Dev01
  • 13,292
  • 19
  • 70
  • 124

4 Answers4

2

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"

Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
  • Thanks for your answer. This was my plan B but I was hoping there was a way to do it with the forEach. Thanks for the help. – Dev01 Apr 21 '17 at 18:05
1

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'))
Matt Way
  • 32,319
  • 10
  • 79
  • 85
1

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");
})();
Lai Xue
  • 1,523
  • 1
  • 15
  • 17
1

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"));