JavaScript Promise.any() Method: Resolving When Any Promise Succeeds

The Promise.any() method in JavaScript is a powerful tool for handling multiple asynchronous operations. It takes an iterable of promises and resolves as soon as any one of the promises fulfills. If all promises reject, it rejects with an AggregateError containing all the rejection reasons. This is particularly useful when you have multiple sources for the same data and want to use the first one that responds successfully.

Purpose and Use Cases

The primary purpose of Promise.any() is to race multiple promises against each other, resolving with the first success. Common use cases include:

  • Redundant Requests: Querying multiple servers for the same resource and using the fastest response.
  • Fallback Strategies: Trying multiple methods to achieve a goal and using the first successful outcome.
  • Parallel Execution: Running multiple independent tasks and proceeding as soon as one completes.

Syntax

Promise.any(iterable);

Parameters

  • iterable: An iterable object (e.g., Array, Set) containing promises.

Return Value

  • A Promise that resolves with the value of the first fulfilled promise in the iterable.
  • If all promises reject, the returned Promise rejects with an AggregateError.

Examples

Basic Example: Resolving with the First Fulfilled Promise

In this example, Promise.any() resolves with the value of the first promise that fulfills.

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => reject("Promise 1 Rejected"), 500);
});

const promise2 = new Promise((resolve) => {
  setTimeout(() => resolve("Promise 2 Resolved"), 300);
});

const promise3 = new Promise((resolve) => {
  setTimeout(() => resolve("Promise 3 Resolved"), 400);
});

Promise.any([promise1, promise2, promise3])
  .then((value) => {
    console.log("Resolved:", value);
  })
  .catch((error) => {
    console.error("Rejected with:", error);
  });

Output:

Resolved: Promise 2 Resolved

In this case, promise2 resolves first, so Promise.any() resolves with its value.

Handling Rejections: AggregateError

If all promises reject, Promise.any() rejects with an AggregateError.

const promise4 = new Promise((resolve, reject) => {
  setTimeout(() => reject("Promise 4 Rejected"), 300);
});

const promise5 = new Promise((resolve, reject) => {
  setTimeout(() => reject("Promise 5 Rejected"), 200);
});

Promise.any([promise4, promise5])
  .then((value) => {
    console.log("Resolved:", value);
  })
  .catch((error) => {
    console.error("Rejected with:", error.errors);
  });

Output:

Rejected with: [ 'Promise 5 Rejected', 'Promise 4 Rejected' ]

Here, both promise4 and promise5 reject, resulting in an AggregateError containing the rejection reasons.

Using with Asynchronous Functions

Promise.any() works seamlessly with asynchronous functions.

async function asyncFunc1() {
  await new Promise((resolve) => setTimeout(resolve, 400));
  return "Async Func 1 Resolved";
}

async function asyncFunc2() {
  await new Promise((resolve) => setTimeout(resolve, 200));
  return "Async Func 2 Resolved";
}

Promise.any([asyncFunc1(), asyncFunc2()])
  .then((value) => {
    console.log("Resolved:", value);
  })
  .catch((error) => {
    console.error("Rejected with:", error);
  });

Output:

Resolved: Async Func 2 Resolved

asyncFunc2 resolves faster, so Promise.any() resolves with its return value.

Real-World Example: Fetching Data from Multiple APIs

Consider a scenario where you want to fetch data from multiple APIs and use the response from the first API that responds successfully.

const api1Url = "https://rickandmortyapi.com/api/character";
const api2Url = "https://api.publicapis.org/random";

const fetchFromAPI1 = () =>
  fetch(api1Url).then((response) => {
    if (!response.ok) {
      throw new Error(`API 1 Failed: ${response.status}`);
    }
    return response.json();
  });

const fetchFromAPI2 = () =>
  fetch(api2Url).then((response) => {
    if (!response.ok) {
      throw new Error(`API 2 Failed: ${response.status}`);
    }
    return response.json();
  });

Promise.any([fetchFromAPI1(), fetchFromAPI2()])
  .then((data) => {
    console.log("Data fetched successfully:", data);
  })
  .catch((error) => {
    console.error("Failed to fetch data from any API:", error);
  });

This example attempts to fetch data from two different APIs. Promise.any() resolves with the data from the first API that responds successfully. If both APIs fail, it catches the AggregateError.

Tips and Considerations

  • Error Handling: Always include a .catch() block to handle the case where all promises reject.
  • Performance: Promise.any() can improve performance by utilizing the fastest successful response from multiple sources.
  • Browser Compatibility: Ensure that you are using a modern browser that supports Promise.any(). If not, consider using polyfills. ℹī¸

Browser Support

The Promise.any() method is supported in modern browsers. However, for older browsers, you might need to use a polyfill to ensure compatibility. đŸ’ģ

| Browser | Version | Support |
| ————– | ——- | ——- |
| Chrome | 85 | Yes |
| Firefox | 79 | Yes |
| Safari | 14 | Yes |
| Edge | 85 | Yes |
| Opera | 71 | Yes |
| Node.js | 15 | Yes |

Conclusion

The Promise.any() method is a valuable addition to JavaScript’s asynchronous programming toolkit. It simplifies the process of racing multiple promises and resolving with the first success, making it ideal for scenarios where redundancy and fallback strategies are crucial. By understanding how to use Promise.any(), you can write more efficient and robust asynchronous code. 🎉