ECMAScript 2021 introduces Promise.any() and AggregateError


ECMAScript 2021 is the latest JavaScript version that introduced multiple new features to the JavaScript language.

In this blog, we will cover Promise.any() method and AggregateError that was a part of ECMAScript 2021 addition.

Promise.any() method accepts a list of Promise objects as an iterable object and, as soon as one of the promises from the list fulfills, it returns a single promise that resolves with the value from that promise.

If no promise in the iterable is fulfilled, then the returned promise is rejected with an AggregateError, which is a new subclass of the Error.

Let’s take an example to understand it better.

const promise1 = fetch("https://jsonplaceholder.typicode.com/todos/1")
  .then(
    (response) => response.json()
  );

const promise2 = fetch("https://jsonplaceholder.typicode.com/todos/2")
  .then(
    (response) => response.json()
  );

const promise3 = fetch("https://jsonplaceholder.typicode.com/todos/3")
  .then(
    (response) => response.json()
  );

Promise.any([promise1, promise2, promise3])
  .then((value) =>console.log(value))
  .catch((error) => console.log(error))

//Output
{
  completed: false,
  id: 1,
  title: "delectus aut autem",
  userId: 1
}

In the above example, we are passing three different promises to Promise.any method for fetching a todo item using a fake API.

promise1 is the first promise to be fulfilled so its value (a todo object) is returned from the Promise.any method and printed to the console.

Syntax

Promise.any(iterable);

Return value

An already rejected Promise if the iterable passed is empty.

Promise.any([]);

//Output
Promise {<rejected>: AggregateError: All promises were rejected}

An asynchronously resolved Promise if the iterable passed contains no promises.

Promise.any([100,200,300]);

//Output
Promise {<fulfilled>: 100}

First resolved value, if any of the promises in the given iterable resolves.

const promiseError = new Promise((resolve, reject) => {
  reject("Error coming!");
});

const promiseSlow = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, "Slow as a tortoise");
});

const promiseFast = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "Fast as a bullet");
});

Promise.any([promiseError, promiseSlow, promiseFast])
.then((value) => {
  console.log(value);
});

//Output "Fast as a bullet"

Error if all the promises are rejected.

const pErr1 = new Promise((resolve, reject) => {
  reject("Always fails");
});

const pErr2 = new Promise((resolve, reject) => {
  reject("I will also fail");
});

Promise.any([pErr1, pErr2]).catch((err) => {
  console.log(err);
});

//Output
AggregateError: All promises were rejected
Promise {<fulfilled>: undefined}

Promise.race vs Promise.any

Promise.race() returns the first settled value (either fulfillment or rejection) from a list of promises while the Promise.any() returns the first fulfilled value.

Let’s take an example to understand it better.

const promise1 = fetch("https://jsonplaceholder.typicode.com/todos/1")
.then(
  (response) => response.json()
);

const promise2 = new Promise((resolve, reject) => {
  reject("I am a rejected value");
});

Promise.race([promise1, promise2])
  .then((value) => console.log(value))
  .catch((value) => console.log(value));

//Output "I am a rejected value"

Promise.any([promise1, promise2])
  .then((value) => console.log(value))
  .catch((value) => console.log(value));

//Output
{
  completed: false,
  id: 1,
  title: "delectus aut autem",
  userId: 1
}

In the above example promise1 will fetch a todo item from an endpoint but, promise2 will be instantly rejected.

As we know, Promise.race returns the first promise that is either resolved or rejected from a list of promises, so the value of promise2 is returned as it is instantly rejected.

But, in the case of Promise.any, the first resolved value is returned, so we get a todo object as an output.

AggregateError

AggregateError is an object that is thrown when multiple errors need to be reported by an operation.

It takes multiple iterable errors instances and wraps them in a single error.

Syntax

AggregateError(Array<Error>, “message”);

Creating an AggregateError

try {
  throw new AggregateError(
    [new Error("an error"), new TypeError("Typeof error for some field")],
    "common error message"
  );
} catch (e) {
  console.log(e instanceof AggregateError); // true
  console.log(e.message); // "common error message"
  console.log(e.name); //"AggregateError"
  console.log(e.errors); //[ Error: "an error", TypeError: Typeof error for some field]
}