Hey there!
Have you ever heard about the Promise.all() function? MDN says about this function:The Promise.all() static method takes an iterable of promises as input and returns a single Promise. This returned promise fulfills when all of the input's promises fulfill (including when an empty iterable is passed), with an array of the fulfillment values. It rejects when any of the input's promises rejects, with this first rejection reason.
What if you want a function that given an array of Promisses will return a Promise that, even when a Promise in the array rejects, will resolve with the array of results? Actually JavaScript has a function called Promise.allSettled(), but it is only compatible with some more recent versions of browsers. MDN says about this function:The Promise.allSettled() static method takes an iterable of promises as input and returns a single Promise. This returned promise fulfills when all of the input's promises settle (including when an empty iterable is passed), with an array of objects that describe the outcome of each promise.
Let's create our personal implementation of Promise.allSettled(), compatible even with the older browsers versions.
Let's take a look to the heart of our implementation:
1let index = 0;
2let results = [];
3
4for (let i = 0; i < promisses.length; i++) {
5 Promise.resolve(promisses[i])
6 .then(res => results[i] = ({ status: "resolved", result: res }))
7 .catch(err => results[i] = ({ status: "rejected", result: err }))
8 .finally(() => {
9 if (++index === promisses.length) {
10 resolve(results);
11 }
12 });
13}
As you can see, we iterate over the Promisses array. Then we try to resolve each promise. If the promise is resolved, we'll add to the array results an object with status'resolved' and the result of the Promise for the result field. If the Promise is rejected, we'll add to the array an object with status 'rejected' and the error returned by the Promise for the result field.This way, the caller can distinguish between successes and errors. When the index variable is equal to the length of the promisses array, we can resolve the Promise with the results array.
Now we should consider the edge cases. For example, what if we give null or undefined to the function insted of an array? I personally think that, in this situation, we should reject the Promise because this is an error situation and we should notify the caller. And if we pass an empty array? This is an absolutely valid input, which represents no Promise to execute. In this case we can resolve the Promise with an empty array.
Let's take a look to the code for handling these two situations:
1if (!promisses) {
2 reject(new Error("Invalid promisses array"));
3} else if (promisses.length === 0) {
4 resolve([]);
5}
But we still have a problem: what if the caller gives this array [1, 'abc']? They are not Promisses, so we can't call then() or catch() directly on them. For handling these situations as well, we'll use the Promise.resolve() function as you can see in the first code block.
Here it is the full implementation of the myAllSettled function:
1function myAllSettled(promisses) {
2 return new Promise((resolve, reject) => {
3 if (!promisses) {
4 reject(new Error("Invalid promisses array"));
5 } else if (promisses.length === 0) {
6 resolve([]);
7 } else {
8 let index = 0;
9 let results = [];
10
11 for (let i = 0; i < promisses.length; i++) {
12 Promise.resolve(promisses[i])
13 .then(res => { results[i] = ({ status: "resolved", result: res }) })
14 .catch(err => { results[i] = ({ status: "rejected", result: err }) })
15 .finally(() => {
16 if (++index === promisses.length) {
17 resolve(results);
18 }
19 });
20 }
21 }
22 });
23}
The myAllSettled() function can be a good solution if you have to deal with older versions of browsers which doesn't support the Promise.allSettled() function.
Fell free to use it in your code and let me know if you have some improvement or doubt about the implementation.
See you!