TLDR: Use
for...ofinstead offorEachin asynchronous code.
Array.prototype.forEach can not be used for asynchronous code. (It could not be used with promises, and it can not be used with async-await.)
For example, the following forEach loop might not do what it appears to do:
const players = await this.getWinners();
players.forEach(async (player) => {
await givePrizeToPlayer(player);
});
await sendEmailToAdmin('All prizes awarded');What's wrong with it?
- The promises returned by the iterator function are not handled. If one of them throws an error, the error won't be caught. (In Node 10, the process might crash!)
- The prizes are awarded in parallel, not serial (one by one).
- The loop finishes iterating before any of the prizes have been awarded.
- As a result, sendEmailToAdmin sends the email before the prizes have been awarded. Maybe none of them will end up being awarded (they might all throw an error)!
Fortunately if your language has async-await then it will also have the for...of construction, so you can use that.
for (const player of players) {
await givePrizeToPlayer(player);
}(You could also use for(...;...;...) but that is more verbose than we need.)
await Promise.all(players.map(async (player) => {
await givePrizeToPlayer(player);
}));Some people recommend this approach as more functional, but personally I find the for-of loop is clearer.
await players.reduce(async (a, player) => {
await a;
await givePrizeToPlayer(player);
}, Promise.resolve());