Promise.all()

Continuing on with a look into JavaScript Promises, this week I'm writing about Promise.all(). Promise.all() is a method that takes an iterable of Promises as its sole argument, and returns a single Promise that resolves to an array of the results of those input Promises.

This returned Promise will either resolve when all of the provided input Promises have resolved or if the iterable provided contains no Promises. Otherwise the returned Promise rejects immediately if any of the provided Promises reject or if a provided non-Promise throws an error. In this case, it is rejected immediately.

Promise.all() is useful for grouping together the results of multiple Promises. If you need multiple asynchronous tasks to complete before some other code execution continues, then you should use Promise.all().

const promise1 = Promise.resolve("first promise done");
const promise2 = "second item done"; // not a Promise!
const promise3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("third promise done"), 2000);
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
});
// expected result (after two seconds):
// ["first promise done", "second item done", "third promise done"]

In this example we have passed an array containing a few Promises to Promise.all(). Note that promise2 contains a non-Promise value. Promise.all() simply ignores this value, but it still comes through in the returned Promise array. After two seconds (since promise3 has a setTimeout function that delays for two seconds), we get an array of our three results.

Syntax

Promise.all(iterable);

Parameters

  • iterable - An iterable object such as an Array.

Return Value

Promise.all() will return one of a few different things depending on the iterable that gets passed:

  • If the iterable passed is empty, then Promise.all() will return an already resolved Promise.
  • If the iterable passed contains no Promises, then Promise.all() will return an asynchronously resolved Promise. Note that Google Chrome 58 returns an already resolved Promise in this case.
  • In all other cases, Promise.all() will return a pending Promise. This returned Promise is then resolved or rejected asynchronously when all of the Promises in the given iterable have resolved, or if any of the Promises reject. The returned values will be in the order in which their Promises were passed in the iterable, regardless of the order in which they were completed.

Fulfillment

The returned Promise is fulfilled with an array containing all of the resolved values (including non-Promise values) from the iterable passed as the argument.

If an empty iterable is passed, then the Promise returned by Promise.all() is fulfilled synchronously, and the resolved value is an empty array. For example:

const p = Promise.all([]); // An empty iterable is passed, this will resolve immediately.
console.log(p);
// expected result:
// Promise { <state>: "fulfilled", <value>: Array[0] }

If a non-empty iterable is passed, and all of the Promises fulfill (or are not Promises), then the Promise returned by Promise.all() is fulfilled asynchronously. We have seen the first case in the example above, but let's look at an example where the iterable only has non-Promise values:

const p = Promise.all(["first non-promise", 12345, true]);
console.log(p);
// expected result:
// Promise { <state>: "pending" }

setTimeout(() => {
  console.log(p);
});
// expected result:
// Promise: { <state>: "fulfilled", <value>: ["first non-promise", 12345, true] }

Rejection

If any of the Promises in the passed iterable reject, Promise.all() asynchronously rejects with the value of the Promise that rejected, whether or not the other Promises have resolved.

Promise.all() provides us with fail-fast behavior, meaning that the method rejects as soon as any of the passed Promises reject. For example:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("first promise done"), 1000);
});
const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("second promise done"), 2000);
});
const promise3 = new Promise((resolve, reject) => {
  reject(new Error("reject!!"));
});
const promise4 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("fourth promise done"), 4000);
});

Promise.all([promise1, promise2, promise3, promise4]).then(
  (values) => console.log(values),
  (error) => console.error(error)
);
// expected result (immediately): "reject!!"

Here we have four Promises, each of which use setTimeout() functions to simulate some time-based code, except for the third Promise, promise3, which rejects immediately by throwing an Error with the error message "reject!!". This rejection happens immediately, which makes Promise.all() reject immediately with the "reject!!" error message. It doesn't wait for the other Promises to finish resolving.

However, it is possible to modify this behavior by handling rejections individually on each Promise passed to Promise.all() using the .catch() instance method:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("first promise done"), 1000);
});
const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("second promise done"), 2000);
});
const promise3 = new Promise((resolve, reject) => {
  reject(new Error("reject!!"));
});
const promise4 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("fourth promise done"), 4000);
});

Promise.all([
  promise1.catch((error) => error),
  promise2.catch((error) => error),
  promise3.catch((error) => error),
  promise4.catch((error) => error),
]).then((values) => {
  console.log(values);
});
// expected result (after four seconds):
// ["first promise done", "second promise done", Error: "reject!!", "fourth promise done"]

From MDN: The Promise.all() method takes an iterable of Promises as an input, and returns a single Promise that resolves to an array of the results of the input Promises. This returned Promise will resolve when all of the input's Promises have resolved, or if the input iterable contains no Promises. It rejects immediately upon any of the input Promises rejecting or non-Promises throwing an error, and will reject with this first rejection message / error.

See more examples and further documentation here.