repost: async / await in forEach loop 🤯. Hey! I recently was working on a… | by Nick Graffis | Medium

Hey! I recently was working on a project that had me looping through an array of table names, and wanted to see if each table existed in a MySQL database. Create one if it didn’t exist, then log something.

For this article I’m going to use a different promised based function, instead of the MySQL query, for simplicity sake.

We will use this function in our loop:

1
const waitFor = (ms) => new Promise((res) => setTimeout(res, ms));

So the first thing I tried was:

1
2
3
4
5
[10, 2, 3].forEach(async (num) => {
await waitFor(num);
console.log(num);
});
console.log("Done");

Seems to make sense, but in reality

1
$ Done$ 2$ 3$ 10

If we take a look at the polyfill for forEach() we get a better idea of what is happening: Array.prototype.forEach() — JavaScript | MDN

Basically it uses a while loop that calls the callback for each entry, but it doesn’t wait for the previous entry to finish.

So we need to create our own asyncForEach():

1
2
3
4
5
Array.prototype.asyncForEach = async function (callback) {
for (let i = 0; i < this.length; i++) {
await callback(this[i]);
}
};

With this new tool we try again:

1
2
3
4
5
[10, 2, 3].asyncForEach(async (num) => {
await waitFor(num);
console.log(num);
});
console.log("Done");

And we get:

1
2
3
4
$ Done
$ 10
$ 2
$ 3

Closer! The asyncForEach() is working great! waiting the 10ms for iteration 0 to finish, before going on to iteration 1.

To get it all to work as expected we can just wrap our execution into an async function:

1
2
3
4
5
6
7
8
const go = async () => {
await [10, 2, 3].asyncForEach(async (num) => {
await waitFor(num);
console.log(num);
});
console.log('Done');
}
go()

And then in response:

1
2
3
4
$ 10
$ 2
$ 3
$ Done

👏 And it works!

Edited on