Awaiting multiple requests to finish using Promise.all

The scenario: you want to make multiple requests at the same time, and wait for them all to finish before returning all the data. Or, alternatively, you don't need to return any data but instead just need them all to execute before the function returns.

Maybe you're looking to batch similar requests into X number at a time.

Or maybe you need to wait for the requests to finish before returning a webpage or response.

Instead of having to do something like:

const response1 = await apiRequest()
const response2 = await apiRequest()
const response3 = await databaseRequest()

return [response1, response2, response3]

Or something like this, where you're looping:

const responses = []

for (let i = 0; i < 50; i++) {
  const data = await apiRequest()
  responses.push(data)
}

...there is a much easier way to handle executing those async functions.

Having to wait for each request to finish before making the next one is a pain... and if your use case is such that it doesn't matter what order they run in, no use in taking the performance hit.

Promise.all() to the rescue!

Promise.all is the native function that will solve this problem for us.

It allows us to pass in an array of Promise/async functions and it will wait for each of them to finish before returning.

So whenever you have async functions that need to be executed together and you need to wait for all of them to finish, use Promise.all.

For example, imagine you're building a user profile portion of a dashboard application. And you need to make several HTTP requests and a database request to fetch the data for the user. But it needs to be returned together.

Let's see what that looks like in code...

const userProfileData = await Promise.all([
  getSettings(user)
  getMetadata(user)
  getDefaultLayoutConfig()
])

// destructured example
const [settings, metadata, defaultConfig] = await Promise.all([
  getSettings(user)
  getMetadata(user)
  getDefaultLayoutConfig()
])

Another benefit of using Promise.all is that it will kickoff the functions at the same time - concurrently - as opposed to starting one, waiting for it to finish, then starting the next one. Read more about that here.

Error handling

A quick note on what happens if one of the Promise/async functions fails when using Promise.all - it uses a "fail-fast" approach, so the first function to fail will cause the rest of the functions to not be executed.

Instead, Promise.all will exit early. And it will return a rejected Promise.

There are ways to handle this differently, using .catch() but I'm keeping this post limited to the topic of making multiple requests. I'll talk about the different ways of error handling Promise.all in a future post.

Wrapping up

So, next time you have a scenario in which you have a related set of data you need to asynchronously request and return - and those functions don't depend on each other, they can be executed at the same time - use Promise.all!

When it comes to Node, mastering Promises is one thing.... but using that knowledge to build out a full REST API is a whole other animal.

To help you hit the ground running and avoid wasting time on figuring out what code goes here when you're building your next Express project, I have a standard template I use to structure all my REST API'S - sign up below to receive the template repo and a post explaining in detail what logic goes where within that structure. You'll also receive all my future posts directly to your inbox!

Subscribe for the repo!

No spam ever. Unsubscribe any time.