JavaScript introduced Promise.allSettled
in ES2020 to make working with multiple asynchronous operations easier. Unlike Promise.all
, which short-circuits when a promise rejects, Promise.allSettled ensures that you get results from all promises, whether they succeed or fail.
In this tutorial, I’ll walk you through creating your own implementation of Promise.allSettled
, focusing on building it from scratch. We will also explore how promises work behind the scenes, helping you understand the asynchronous behavior that makes JavaScript so powerful.
What is Promise.allSettled
?
Before we jump into writing code, let's break down what Promise.allSettled
does:
- Input: An array of promises.
- Output: A promise that resolves after all input promises have settled (either fulfilled or rejected), with an array of objects describing the result of each promise.
Each object in the array contains:
-
status
:"fulfilled"
or"rejected"
. -
value: The value if the promise resolved, or reason if it
rejected
. Example:
const promises = [
Promise.resolve('Success'),
Promise.reject('Failure'),
Promise.resolve('Complete')
];
Promise.allSettled(promises).then(results => {
console.log(results);
});
Output:
[
{ status: 'fulfilled', value: 'Success' },
{ status: 'rejected', reason: 'Failure' },
{ status: 'fulfilled', value: 'Complete' }
]
This method is ideal when you need to wait for all promises to finish, regardless of whether they succeed or fail.
Why Implement Promise.allSettled
Yourself?
Even though this feature is now available in modern browsers, implementing it yourself offers a deeper understanding of how JavaScript promises work. Plus, it ensures compatibility with older environments that don’t support ES2020 features natively.
Step-by-Step Guide to Implement Promise.allSettled
We’re going to create a function named allSettled
that mimics the behavior of Promise.allSettled. Let’s build this step-by-step:
Step 1: Create a Wrapper Function
The function takes an array of promises and returns a new promise. This new promise will resolve when all the input promises settle (either resolve or reject).
function allSettled(promises) {
// This will return a new promise that resolves when all promises settle
return new Promise((resolve) => {
// Implementation goes here
});
}
Step 2: Handle Each Promise
For each promise in the array, we need to track whether it resolves or rejects. We’ll wrap each promise with a .then()
and .catch()
to capture its status:
function allSettled(promises) {
return new Promise((resolve) => {
let results = [];
let count = 0;
promises.forEach((promise, index) => {
// Wrap the promise with a .then() and .catch() to capture the result
promise
.then((value) => {
results[index] = { status: 'fulfilled', value };
})
.catch((reason) => {
results[index] = { status: 'rejected', reason };
})
.finally(() => {
count++;
// Once all promises have settled, resolve the outer promise
if (count === promises.length) {
resolve(results);
}
});
});
});
}
Explanation:
1. Creating the Outer Promise:
The allSettled function returns a new Promise. This promise will resolve when all input promises have settled.
2. Looping Over Promises:
We loop through the promises array using .forEach. For each promise, we track its outcome with
.then()
(for resolved promises) and.catch()
(for rejected promises).
3. Recording Results:
The result of each promise is stored in the results array. If the promise resolves, the object contains { status:
'fulfilled'
, value }. If it rejects, it stores { status:'rejected'
, reason }.
4. Counting Settled Promises:
We use a count variable to track how many promises have settled. Each time a promise finishes (either through
.then()
or.catch()
), we increment the count. Once count equals the length of the input array, we resolve the outer promise with the results array.
Step 3: Testing Our allSettled Function
Now, let's test this custom implementation:
const promises = [
Promise.resolve('Task 1 completed'),
Promise.reject('Task 2 failed'),
Promise.resolve('Task 3 completed')
];
allSettled(promises).then((results) => {
console.log(results);
});
Output:
[
{ status: 'fulfilled', value: 'Task 1 completed' },
{ status: 'rejected', reason: 'Task 2 failed' },
{ status: 'fulfilled', value: 'Task 3 completed' }
]
As expected, the function waits for all promises to settle, and the results array gives us detailed information about each promise, including whether it resolved or rejected.
A Deeper Look Into Promises
To reinforce understanding, let's break down how promises work:
- Promises represent the eventual result of an asynchronous operation. When a promise is created, it is in a pending state.
- A promise can be:
Resolved (fulfilled) when the operation completes successfully.
Rejected when the operation fails.
Settled when it is either fulfilled or rejected
The then()
method allows us to specify a function to be executed when the promise is resolved, while catch()
allows us to handle rejections. Using finally()
ensures that we handle the promise settling (either success or failure) without duplication.
Conclusion
Implementing Promise.allSettled
yourself is a fantastic way to understand how promises work at a fundamental level. The key takeaway is that Promise.allSettled
waits for all promises to finish, unlike Promise.all
, which stops when it encounters a rejection.
By implementing this from scratch, you’ve also learned how to handle multiple asynchronous operations in a clean and efficient way. Now you can use this knowledge to work with promises in any JavaScript environment, including those that don’t natively support modern features.
Top comments (0)