JavaScript Promise.allSettled() Method: Resolving All Promises Regardless of Results

The Promise.allSettled() method in JavaScript is a powerful tool for handling multiple promises concurrently, ensuring that you can process the results of all promises, irrespective of whether they fulfill or reject. This method is particularly useful when you need to perform operations that depend on the outcome of several asynchronous tasks, and you want to know the status and result of each one.

What is Promise.allSettled()?

Promise.allSettled() takes an iterable of promises as input and returns a single promise that resolves after all the input promises have either fulfilled or rejected. The resolved value is an array of objects, each describing the outcome of each promise. Each object has a status property, which can be either "fulfilled" or "rejected", and a value (if fulfilled) or reason (if rejected) property.

Purpose of Promise.allSettled()

The primary purpose of Promise.allSettled() is to handle scenarios where you need to execute multiple asynchronous operations and process their results without being interrupted by a rejection. This is particularly useful in situations like:

  • Monitoring multiple API requests: Ensuring that you know the status of all API calls, even if some fail.
  • Batch processing: Performing multiple operations and collecting the results, regardless of individual success or failure.
  • Parallel execution with comprehensive reporting: Running tasks in parallel and providing a complete report of the outcomes.

Syntax of Promise.allSettled()

The syntax for Promise.allSettled() is straightforward:

Promise.allSettled(iterable);

Parameters:

  • iterable: An iterable object (like an array) containing promises.

Return Value:

  • A Promise that resolves with an array of objects, each describing the outcome of an input promise. Each object has the following structure:

  • For fulfilled promises:
    json
    { status: "fulfilled", value: value }

  • For rejected promises:
    json
    { status: "rejected", reason: reason }

Attributes Table

Attribute Type Description
`iterable` Iterable (e.g., Array) An iterable object containing promises to be settled.
`status` String The status of the promise, either `”fulfilled”` or `”rejected”`.
`value` Any The fulfilled value of the promise, present if the status is `”fulfilled”`.
`reason` Any The rejection reason of the promise, present if the status is `”rejected”`.

Basic Examples of Promise.allSettled()

Let’s start with some basic examples to illustrate how Promise.allSettled() works.

Example 1: Handling Mixed Resolved and Rejected Promises

In this example, we have an array of promises that includes both resolved and rejected promises.

const promise1_allsettled = Promise.resolve(1);
const promise2_allsettled = Promise.reject("Error occurred");
const promise3_allsettled = new Promise((resolve) =>
  setTimeout(() => resolve(3), 100)
);

Promise.allSettled([promise1_allsettled, promise2_allsettled, promise3_allsettled])
  .then((results) => console.log(results));

Output:

[
  { "status": "fulfilled", "value": 1 },
  { "status": "rejected", "reason": "Error occurred" },
  { "status": "fulfilled", "value": 3 }
]

In this output, you can see that each promise’s outcome is clearly indicated with its status and either its value or reason for rejection.

Example 2: Processing Results After All Promises Settle

Here, we process the results to count the number of fulfilled and rejected promises.

const promise1_allsettled_2 = Promise.resolve(1);
const promise2_allsettled_2 = Promise.reject("Error occurred");
const promise3_allsettled_2 = new Promise((resolve) =>
  setTimeout(() => resolve(3), 100)
);

Promise.allSettled([promise1_allsettled_2, promise2_allsettled_2, promise3_allsettled_2])
  .then((results) => {
    let fulfilledCount = 0;
    let rejectedCount = 0;

    results.forEach((result) => {
      if (result.status === "fulfilled") {
        fulfilledCount++;
      } else if (result.status === "rejected") {
        rejectedCount++;
      }
    });

    console.log(`Fulfilled: ${fulfilledCount}, Rejected: ${rejectedCount}`);
  });

Output:

Fulfilled: 2, Rejected: 1

This output shows the number of promises that were fulfilled and rejected.

Example 3: Using Promise.allSettled() with API Calls

In this example, we simulate making multiple API calls and handle the results.

function simulateApiCall_allsettled(url, success) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (success) {
        resolve(`Data from ${url}`);
      } else {
        reject(`Failed to fetch from ${url}`);
      }
    }, 500);
  });
}

const apiCall1_allsettled = simulateApiCall_allsettled("api/data1", true);
const apiCall2_allsettled = simulateApiCall_allsettled("api/data2", false);
const apiCall3_allsettled = simulateApiCall_allsettled("api/data3", true);

Promise.allSettled([apiCall1_allsettled, apiCall2_allsettled, apiCall3_allsettled])
  .then((results) => {
    results.forEach((result) => {
      if (result.status === "fulfilled") {
        console.log(`Success: ${result.value}`);
      } else {
        console.error(`Error: ${result.reason}`);
      }
    });
  });

Output:

Success: Data from api/data1
Error: Failed to fetch from api/data2
Success: Data from api/data3

This output shows which API calls succeeded and which failed, along with their respective results or reasons for failure.

Advanced Usage of Promise.allSettled()

Filtering Fulfilled and Rejected Promises

You can filter the results to process fulfilled and rejected promises separately.

const promise1_allsettled_adv = Promise.resolve(1);
const promise2_allsettled_adv = Promise.reject("Error occurred");
const promise3_allsettled_adv = new Promise((resolve) =>
  setTimeout(() => resolve(3), 100)
);

Promise.allSettled([promise1_allsettled_adv, promise2_allsettled_adv, promise3_allsettled_adv])
  .then((results) => {
    const fulfilled = results
      .filter((result) => result.status === "fulfilled")
      .map((result) => result.value);
    const rejected = results
      .filter((result) => result.status === "rejected")
      .map((result) => result.reason);

    console.log("Fulfilled values:", fulfilled);
    console.log("Rejected reasons:", rejected);
  });

Output:

Fulfilled values: [1, 3]
Rejected reasons: ["Error occurred"]

Using Promise.allSettled() with Asynchronous Functions

You can use Promise.allSettled() with asynchronous functions to handle multiple asynchronous operations.

async function asyncTask_allsettled(id, success) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (success) {
        resolve(`Task ${id} completed`);
      } else {
        reject(`Task ${id} failed`);
      }
    }, 200);
  });
}

async function processTasks_allsettled() {
  const tasks = [
    asyncTask_allsettled(1, true),
    asyncTask_allsettled(2, false),
    asyncTask_allsettled(3, true),
  ];

  const results = await Promise.allSettled(tasks);

  results.forEach((result) => {
    if (result.status === "fulfilled") {
      console.log(`Success: ${result.value}`);
    } else {
      console.error(`Error: ${result.reason}`);
    }
  });
}

processTasks_allsettled();

Output:

Success: Task 1 completed
Error: Task 2 failed
Success: Task 3 completed

This example demonstrates how to use Promise.allSettled() with asynchronous functions to handle multiple tasks and process their results.

Real-World Applications of Promise.allSettled()

Promise.allSettled() is invaluable in scenarios where you need to handle multiple asynchronous operations and ensure that you process all results, regardless of individual success or failure.

Performing Multiple API Requests

Consider a scenario where you need to fetch data from multiple APIs to populate a dashboard. Using Promise.allSettled(), you can ensure that you handle the results of all API calls, even if some fail.

async function fetchUserData_allsettled(userId) {
  try {
    const response = await fetch(`https://api.example.com/user/${userId}`);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    throw new Error(`Failed to fetch user data for ${userId}: ${error.message}`);
  }
}

async function fetchPostData_allsettled(postId) {
  try {
    const response = await fetch(`https://api.example.com/post/${postId}`);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    throw new Error(`Failed to fetch post data for ${postId}: ${error.message}`);
  }
}

async function populateDashboard_allsettled() {
  const userIds = [1, 2, 3];
  const postIds = [101, 102, 103];

  const userPromises = userIds.map(id => fetchUserData_allsettled(id));
  const postPromises = postIds.map(id => fetchPostData_allsettled(id));

  const allPromises = [...userPromises, ...postPromises];

  const results = await Promise.allSettled(allPromises);

  const userData = [];
  const postData = [];

  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      if (index < userIds.length) {
        userData.push(result.value);
      } else {
        postData.push(result.value);
      }
    } else {
      console.error(`Failed to fetch data: ${result.reason}`);
    }
  });

  console.log('User Data:', userData);
  console.log('Post Data:', postData);
}

populateDashboard_allsettled();

In this example, we fetch user data and post data from different APIs. Using Promise.allSettled(), we ensure that we handle the results of all API calls, logging any errors and processing the successful responses to populate the dashboard.

Batch Processing and Comprehensive Reporting

In scenarios involving batch processing, you might need to perform multiple operations and provide a comprehensive report of the outcomes. Promise.allSettled() is ideal for this purpose.

async function processItem_allsettled(item) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = Math.random() > 0.5; // Simulate success/failure
      if (success) {
        resolve(`Processed item: ${item}`);
      } else {
        reject(`Failed to process item: ${item}`);
      }
    }, 300);
  });
}

async function batchProcess_allsettled(items) {
  const itemPromises = items.map(item => processItem_allsettled(item));
  const results = await Promise.allSettled(itemPromises);

  const processedItems = [];
  const failedItems = [];

  results.forEach((result) => {
    if (result.status === 'fulfilled') {
      processedItems.push(result.value);
    } else {
      failedItems.push(result.reason);
    }
  });

  console.log('Processed Items:', processedItems);
  console.log('Failed Items:', failedItems);
}

const itemsToProcess = ['item1', 'item2', 'item3', 'item4', 'item5'];
batchProcess_allsettled(itemsToProcess);

In this example, we process a batch of items and simulate success or failure for each item. Using Promise.allSettled(), we ensure that we gather the results for all items, providing a report of the processed and failed items.

Browser Support

The Promise.allSettled() method is supported by all modern browsers, including:

  • Chrome
  • Firefox
  • Safari
  • Edge
  • Opera

Conclusion

The Promise.allSettled() method is a valuable addition to JavaScript’s asynchronous programming toolkit. It allows you to handle multiple promises and process their results without being interrupted by rejections, making it ideal for scenarios where you need to ensure that all asynchronous operations are accounted for. By using Promise.allSettled(), you can write more robust and reliable asynchronous code. 🚀