JavaScript: async/await with forEach()

async/await
is freaking awesome, but there is one place where it’s tricky: inside a forEach()
Let’s try something:
const waitFor = (ms) => new Promise(r => setTimeout(r, ms));[1, 2, 3].forEach(async (num) => {
await waitFor(50);
console.log(num);
});console.log('Done');
If you run this code with node.js (≥ 7.6.0), this will happen:
$ node forEach.js
$ Done
What?
console.log(num)
are not displayed into the console.
Let’s re-create forEach()
to understand what’s happening:
Array.prototype.forEach = function (callback) {
// this represents our array
for (let index = 0; index < this.length; index++) {
// We call the callback for each entry
callback(this[index], index, this);
}
};
The real polyfill of forEach()
is available at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#Polyfill
As you can see, thecallback
is called but we are not waiting for it to be done before going to the next entry of the array.
We can solve this by creating our own asyncForEach()
method:
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
Then, we can update our example to use our asyncForEach
method:
asyncForEach([1, 2, 3], async (num) => {
await waitFor(50);
console.log(num);
})console.log('Done');
By running this code into node, we can now see:
$ node forEach.js
$ Done
$ 1
$ 2
$ 3
We’re getting closer!
Actually, our asyncForEach
returns a Promise
(since it’s wrapped into an async
function) but we are not waiting for it to be done before logging ‘Done’.
Let’s update our example again to wrap our execution into an async
method:
const start = async () => {
await asyncForEach([1, 2, 3], async (num) => {
await waitFor(50);
console.log(num);
});
console.log('Done');
}start();
Let’s run it one last time:
$ node forEach.js
$ 1
$ 2
$ 3
$ Done
🎉 We now have a way of using forEach
with async/await
🎉
You can see the full code on https://gist.github.com/Atinux/fd2bcce63e44a7d3addddc166ce93fb2
Credit: Photo taken from https://unsplash.com/photos/YvkH8R1zoQM