Skip to main content Accessibility Feedback

Waiting for multiple all API responses to complete with the vanilla JS Promise.all() method

In yesterday’s article, I mentioned that I recently worked on a pretty big vanilla JS app.

On some pages, we needed to wait for multiple API requests to complete before rendering content. Today, I want to show you a simple way to do that with the Promise.all() method.

How I learned this

As I was trying to figure this out, I discovered that Steve Griffith (who’s video on composition vs. inheritance I shared the other day) also had a video on using fetch with Promise.all().

If you learn better from video, or want another source to complement what I’m going to share today, go check it out. It’s really well done!

How it works

The Promise.all() method accepts an array of promises, and let’s you run callback functions after all of them resolve or one of them throws an error.

You can use it with promise-based XHR (my preferred approach), but for simplicity today I’ll be using fetch() with JSONPlaceholder in my examples.

Let’s imagine you wanted to get back data from two API endpoints:

  • /posts to get a list of blog posts
  • /users to get back a list of users

The /posts endpoint provides user IDs for it’s authors, so you need data from the /users endpoint to display author information.

Getting a single endpoint

Getting data from a single API is relatively straightforward. To get data back from the /posts endpoint, you would do this.

fetch('https://jsonplaceholder.typicode.com/posts')
	.then(function (response) {
		// Get a JSON object from the response
		// This is a weird quirk of Fetch
		return response.json();
	}).then(function (data) {
		// Log the data to the console
		// You would do something with the /posts data here
 		console.log(data);
	}).catch(function (error) {
		// if there's an error, log it
		console.log(error);
	});

Calling multiple APIs in sequence

Promises are designed to prevent callback hell.

You could make your API calls in a sequence, cache the response of each one to a variable, and then do something with them when they’re done.

Here’s what that would look like.

var posts, users;

fetch('https://jsonplaceholder.typicode.com/posts')
	.then(function (response) {
		// Get a JSON object from the response
		// This is a weird quirk of Fetch
		return response.json();
	}).then(function (data) {

		// Log the data to the console
 		console.log(data);

 		// Cache the data to a variable
 		posts = data;

 		// Make another API call and pass it into the stream
 		return fetch('https://jsonplaceholder.typicode.com/users');

	}).then(function (response) {
		// Get a JSON object from the response
		return response.json();
	}).then(function (data) {

		// Log the data to the console
		console.log(data);

		// Cache the data to a variable
		users = data;

		// Now that you have both APIs back, you can do something with the data

	}).catch(function (error) {
		// if there's an error, log it
		console.log(error);
	});

As you can see, though, this is long and kind of awkward. It also means that you need to wait for one API responses to complete before the next can begin, which is a big inefficient.

Let’s look at a better way.

Calling multiple APIs at once

With the Promise.all() method, we can pass in an array of promises. When all of them have resolved (or one fails), it will run our callback functions.

In this case, we would pass in an array of our fetch calls.

Promise.all([
	fetch('https://jsonplaceholder.typicode.com/posts'),
	fetch('https://jsonplaceholder.typicode.com/users')
]);

When they’re all completed, Promise.all() passes along an array of promises to our first .then() callback.

To get a JSON object from each one to pass on, we can use the Array.map() method to create a new array. We also need to wrap that in Promise.all(), since response.json() returns a promise as well.

The data argument in our second then() callback is an array of API data, with each item matching the corresponding API call in the Promise.all() array. In this example, the item at index 0 is for /posts, and the item at index 1 is for /users.

Promise.all([
	fetch('https://jsonplaceholder.typicode.com/posts'),
	fetch('https://jsonplaceholder.typicode.com/users')
]).then(function (responses) {
	// Get a JSON object from each of the responses
	return Promise.all(responses.map(function (response) {
		return response.json();
	}));
}).then(function (data) {
	// Log the data to the console
	// You would do something with both sets of data here
	console.log(data);
}).catch(function (error) {
	// if there's an error, log it
	console.log(error);
});

As you can see, this is a lot more condensed than the previous example.

It also means you can make both calls at once, improving the overall performance of your app.

Try this out yourself

You can copy/paste any of the examples above into the console tab in developer tools to try them out. They’ll work on any web page.

Browser compatibility

The Promise.all() method (and promises in general) work in all modern browsers, but have no IE support.

There are several polyfills available. I use the auto version of this one from Stefan Penner.