Have you ever come across such a scenario where you have to make multiple requests and perform an action where all of these requests are fulfilled? Or perhaps, you want to check how many requests are resolved and how many of them are rejected?
In most of the cases, you are going to use Promise.all a powerful combinator which is helpful when handling multiple requests with promises. But there are also some drawbacks to using this combinator. To explain what this means, let’s take a look at Fig 1(a).
Assume you have some API endpoints in the URLs array, and have sent requests to all of them. If you are using Promise.all, and even a single request is rejected then there will be a short circuit and the rest of the requests will be lost. You will also be unable to track the rest of the requests because the code block will fall into the catch case of the try catch statement.
Due to these scenarios, Jason Williams (BBC), Robert Pamely (Bloomberg), Mathias Bynens (Google) and the champion Mathias Bynens (Google) proposed a new promise combinator known as the Promise.allSettled.
What Exactly is Promise.allSettled?
Promise.allSettled() returns a promise that resolves after all of the given promises have either been resolved or rejected, with an array of objects that each describes the outcome of each promise.
Let’s modify the example given below:
Use Case # 1:
We have passed the requests array to Promise.allSettled and called finally(). This implementation is completely free from short circuits. The flow will never stop due to rejection. Therefore, there will be no need to write try catch statements and extra checks. Looks cool right? Why don’t we look at another use case then!
Use Case # 2:
What if we have to track what requests are fulfilled and which ones have been rejected? Traditionally, we would do it with the help of a helper function which takes a promise and returns its states to either fulfilled or rejected.
If you glance at the example given below, you will see that we have created a helper function named reflect.
Here, this function takes a promise as an argument and calls it in the return statement. If the promise is successfully resolved then it will fall into the first callback function of .then() block which is known as the onFulfilled.
Using this, we are returning the fulfilled value with the status of fulfilled from onFulfilled callback function. If the promise is rejected then it will fall into the second callback function which is known as onRejected. Here, we are returning the error and the status which is rejected from this callback.
Next up, we are going to pass reflect as the callback function to promises.map() which will then map over all the promises and return an array of all the results. For checking successful promises, we are filtering the results with the help of status. The successfulPromises array will contain all the fulfilled requests. And as you can see, this is a lot of work!
Let’s see how Promise.allSettled can simplify this process.
At first sight, you can feel the difference between the traditional approach vs using Promise.allSettled. There is no need to write your own implementation.
Promise.allSettled will map over the promises and will return an array that will contain all the results either fulfilled or rejected. We can filter the request just as we did in the traditional approach. All of this is clean, readable and much more maintainable.