<- back

javascript async looping


Quite often I run across developers who are confused about how to properly loop over an async function in javascript/typescript.

To be honest, this also tripped me up for a long time until it finally clicked.

Here's what I'm talking about:

Say you have an async function like this:

const foo = async (param) => {
 await new Promise((resolve) => {
  setTimeout(() => {
   console.log(`Running for ${param}`);
   return resolve(true);
  }, 1000);

and you have an array like this:

const myArray = [0, 1, 2, 3, 4];

In this example let's assume that you want to run the function foo for each element in myArray and you want to run it serially, meaning run it for element 0, then element 1, then element 2, etc

You may naturally code something that looks like this:

myArray.forEach(async (param) => {  await foo(param); }); console.log('Done!');

or like this:

myArray.map(async (param) => await foo(param)); console.log('Done!');

but both of these won't work! Both of these print the following:

Running for 0
Running for 1
Running for 2
Running for 3
Running for 4

Worse, each of the Running for x messages print essentially simultaneously. There's no 1 second delay between them like you might hope for/expect.

The reason for this is that forEach and map don't wait for the async function to complete before moving on to the next element in the array. They just fire off the async function and move on to the next element.

So what other options are there?

I'm sure there are several, but my go to is this:

for (let i = 0; i < myArray.length; i++) {
 await foo(myArray[i]);

This prints the following:

Running for 0
Running for 1
Running for 2
Running for 3
Running for 4

In addtion, there's now a 1 second delay between each Running for x like we hoped for!

The reason this works is that the await in this for loop is at the "top level of execution" and isn't enclosed within a function. This pauses the main thread of execution until foo resolves.