Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Several promises at once

You often need to run several asynchronous actions at the same time: load a profile and a list of orders, query three servers. For this, the Promise object has static methods that take an array of promises and return one combined promise. They are called combinators.

Every example in this lesson uses a small wait() function: it returns a promise that is fulfilled with value after ms milliseconds.

Promise.all

Promise.all() waits until all the promises are fulfilled and gives an array of results in the same order as the promises:

const wait = (value, ms) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
 
Promise.all([wait('a', 300), wait('b', 100)]).then((values) => {
  console.log(values.join(', ')); // a, b
});

Both waits run at the same time, so the whole thing takes 300 ms, not 400.

If one promise fails

If even one promise is rejected, Promise.all() is rejected straight away with its error, and the other results are lost:

const wait = (value, ms) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
 
Promise.all([wait(1, 100), Promise.reject(new Error('Request 2 failed')), wait(3, 200)])
  .then((values) => console.log(values.join(', ')))
  .catch((error) => console.log(error.message)); // Request 2 failed

This is handy when there is no point in continuing without any one of the results.

Promise.allSettled

Promise.allSettled() waits for all promises, even rejected ones. Each result is an object with a status field: 'fulfilled' with a value, or 'rejected' with an error in reason:

Promise.allSettled([Promise.resolve(10), Promise.reject(new Error('No connection'))]).then((results) => {
  for (const result of results) {
    console.log(result.status);
  }
});
// fulfilled
// rejected

Use it when you want to show everything that loaded and, separately, what did not.

Promise.race

Promise.race() settles together with the first promise to settle — whether it succeeded or failed. This is how a time limit is made:

const wait = (value, ms) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
const timeout = (ms) => new Promise((resolve, reject) => setTimeout(() => reject(new Error('Time is up')), ms));
 
Promise.race([wait('Server response', 500), timeout(200)])
  .then((value) => console.log(value))
  .catch((error) => console.log(error.message)); // Time is up

Promise.any

Promise.any() waits for the first successful promise and ignores errors. It is rejected only if all of them fail:

const wait = (value, ms) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
 
Promise.any([Promise.reject(new Error('Server 1 is down')), wait('Server 2', 300), wait('Server 3', 100)])
  .then((value) => console.log(value)); // Server 3
MethodFulfilled whenRejected when
Promise.all()All succeedThe first error
Promise.allSettled()All have settledNever
Promise.race()The first to settle succeededThe first to settle failed
Promise.any()The first successAll failed

Common mistake

Passing the promises as separate arguments instead of an array. Promise.all(first, second) waits for neither of them and is rejected straight away with a TypeError:

const first = Promise.resolve(1);
const second = Promise.resolve(2);
 
Promise.all(first, second).catch((error) => console.log(error.name)); // TypeError

The correct form uses square brackets: Promise.all([first, second]).

Thanks for reading!

Donate to the site — we work hard for you.