Promise.allSettled
Promise.allSettled returns a promise when all inputs are settled
that is either fulfilled
or rejected
.
Points to remember:
- Returns a promise that contains an array of results.
- Each result object will have two properties (status & reason/value).
- status will be either fulfilled or rejected.
- value if fulfilled or a reason if rejected.
Difference between Promise.all
& Promise.allSettled
Promise.all
will reject as soon as one of the items in the input is rejected.Promise.allSettled
will never reject and will wait until all inputs are settled.
Use cases:
- Make several APIs and wait until all of them are settled 🤔.
I am not so sure of many use-cases. But, we want to know how it works right?. In the below example, first, we will see how it works and then will implement it.
var APICall1 = fetch('https://reqres.in/api/users?page=1');
var APICall2 = fetch('https://reqres.in/api/users?page=2');
Promise.allSettled([APICall1, APICall2]).then((results) => {
results.forEach((result) => {
console.log(result); // Result is below
});
});
// Log:
// {status: "fulfilled", value: Response}
// {status: "fulfilled", value: Response}
Below is a simplified version of TC39 specs to understand its implementation details.
allSettled specification says
- Accepts
iterable
object as an argument. - If
not iterable
,throw
anexception
with an error message. - Iterate the argument if it is
iterable
. - Keep the
results
in an array. - Wait for all promises to either get
resolved
/rejected
. - Return the
results
.
First, we will write a function to find out if the passed value is iterable
or not. We can use [Symbol.iterator]
to find it out. Read more about the iterable here.
function isIterable(value) {
// If no argument is passed or === null
if (arguments.length === 0 || value === null) {
return false;
}
return typeof value[Symbol.iterator] === 'function';
}
isIterable({}); // false
isIterable(1); // false
isIterable(nul); // false
isIterable([]); // true
isIterable(''); // true
// String, Array, TypedArray, Map & Set are all built-in iterables
Next step is to wrap our iteration (forEach) with new Promise()
object. So that when all our inputs are settled, we can return the results with status and value/reasons.
Implementation is quite similar to Promise.all() with a few changes.
// Our custom all settled function
function isAllPromiseSettled(promises) {
// To store our results
var results = Array(promises.length);
// To keep track of how many promises resolved
var counter = 0;
// If not iterable throw an error
if (!isIterable(promises)) {
throw new Error(`${typeof promises} is not iterable`);
}
// Wrapping our iteration with Promise object
// So that we can resolve and return the results on done.
return new Promise((resolve) => {
// Iterate the inputs
promises.forEach((promise, index) => {
// Wait for each promise to resolve
return Promise.resolve(promise)
.then((result) => {
counter++; // Increment counter
// Store status and result in same order
results[index] = { status: 'fulfilled', value: result };
// If all inputs are settled, return the results
if (counter === promises.length) {
resolve(results);
}
})
.catch((err) => {
counter++; // Increment counter
// Store status and reason for rejection in same order
results[index] = { status: 'rejected', reason: err };
// If all inputs are settled, return the results
if (counter === promises.length) {
resolve(results);
}
});
});
});
}
Conclusion
Yep, we did it. We learned what is Promise.allSettled()
and implemented it. If you want to use it today, the browser support is pretty much available in all modern browsers.
The browser implementation might be different than ours. Nevertheless, you get the idea, right?
Thanks for reading till the end 😬. My next post is about Event Emitters
(Pub/Sub). If you are not subscribed, subscribe below. See you next Tuesday ;)
Helpful links
- TC39 specs for promise.allSettled.
- States and Fates of promises