The Pitfalls of Async/Await in Array Loops

Tory Walker
Mar 8, 2019 · 4 min read

Using async/await while looping through arrays in Javascript loop seems simple, but there’s some non-intuitive behavior to look out for when combining the two. Let’s take a look at three different examples to see what you should look out for, and which loop is best for specific use cases.


Received Todo 2, Response: { ··· }
Received Todo 1, Response: { ··· }
Received Todo 3, Response: { ··· }

⚠️ Problem 1:

The code above will happily execute. However, notice that Finished! was logged out first despite our use of await before urls.forEach. The first problem is that you can’t await the entire loop when using forEach.

⚠️ ️Problem 2:

In addition, despite the use of await within the loop, it didn’t wait for each request to finish before executing the next one. So, the requests were logged out of order. If the first request takes longer than the following requests, it could still finish last.

For both of those reasons, forEach should not be relied upon if you’re using async/await.


Received Todo 1, Response: { ··· }
Received Todo 2, Response: { ··· }
Received Todo 3, Response: { ··· }

We’ve solved the issue of waiting on every request to finish before continuing onward. It also appears that we resolved the issue of the requests happening out of order, but that’s not exactly the case.

As mentioned earlier, Promise.all executes all of the promises given to it in parallel. It won’t wait for the first request to come back before executing the second, or third request. For most purposes this is fine, and it’s a very performant solution. But, if you truly need each request to happen in order, Promise.all won’t solve for that.


The for...of loop executes in the order one would expect — waiting on each previous await operation to complete before moving on to the next:

Received Todo 1, Response: { ··· }
Received Todo 2, Response: { ··· }
Received Todo 3, Response: { ··· }

I particularly like how this method allows the code to remain linear — which is one of the key benefits of using async/await. I find it much easier to read than the alternatives.

If you don’t need access to the index, the code becomes even more concise:

for (const url of urls) { ··· }

One of the major downsides of using a for...of loop is that it performs poorly compared to the other looping options in Javascript. However, the performance argument is negligible when using it to await asynchronous calls, since the intention is to hold up the loop until each call resolves. I typically only use for...of if asynchronous order of execution matters.

Note: You can also use basic for loops to get all of the benefits of for...of, but I like the simplicity and readability that for...of brings to the table.

👏 If you found this article helpful and would like to see more, please let me know by leaving some claps! 🔗 Follow for more articles like this.


JavaScript news and opinion.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store