Handling Errors in Promise.All

Handling errors in Promise.all

Promise.all is all or nothing. It resolves once all promises in the array resolve, or reject as soon as one of them rejects. In other words, it either resolves with an array of all resolved values, or rejects with a single error.

Some libraries have something called Promise.when, which I understand would instead wait for all promises in the array to either resolve or reject, but I'm not familiar with it, and it's not in ES6.

Your code

I agree with others here that your fix should work. It should resolve with an array that may contain a mix of successful values and errors objects. It's unusual to pass error objects in the success-path but assuming your code is expecting them, I see no problem with it.

The only reason I can think of why it would "not resolve" is that it's failing in code you're not showing us and the reason you're not seeing any error message about this is because this promise chain is not terminated with a final catch (as far as what you're showing us anyway).

I've taken the liberty of factoring out the "existing chain" from your example and terminating the chain with a catch. This may not be right for you, but for people reading this, it's important to always either return or terminate chains, or potential errors, even coding errors, will get hidden (which is what I suspect happened here):

Promise.all(state.routes.map(function(route) {
return route.handler.promiseHandler().catch(function(err) {
return err;
});
}))
.then(function(arrayOfValuesOrErrors) {
// handling of my array containing values and/or errors.
})
.catch(function(err) {
console.log(err.message); // some coding error in handling happened
});

Promise.all error-handling syntax when async func is passed as argument

Write another function for the .map to do the same thing you're doing in the first code block: invoke handler and .catch if it throws:

const results = await Promise.all(
items.map(
item => handler(item).catch(error => null)
)
);

Or, with Promise.allSettled:

const results = await Promise.allSettled(items.map(handler));

The above will result in an array of objects. To find the results that went through successfully, extract the value from the objects that have it:

const truthyResults = results
.map(({ value }) => value)
.filter(value => value !== undefined);

Can I error handle await Promise.all with a try-catch block?

try/catch is a correct way to catch errors from Promise.all, but at the same time, it will ignore all fulfilled requests once only a single request failed, which is not ideal if you still want to have other successful requests' data instead of logging errors, so I'd suggest you use Promise.allSettled

With this solution, it will keep all requests including failed ones and successful ones

const [groupedMeditations, meditationPreferences] = await Promise.allSettled([
getMeditationsByGroup(),
getAllPreferences(),
]);

A possible result can be

[
{status: "fulfilled", value: "successful value"}, // your 1st request passed
{status: "rejected", reason: "Error: an error"} //your 2nd request failed
]

From that response, you can filter or log errors

Just one side note that Promise.allSettled does not work for IE, so you need to have polyfill to overcome that

Hopefully, it's helpful for your case :D

JS Promises.all() - how to prevent a 'catch' from stopping the promise chain?

Yes, all you need to do is return something from the catch of the failed promise and it will be "recovered" as a resolve in the Promise.all

Hopefully this demonstrates. Without the catch in somePromisyFunction2 the whole chain will fail (check by commenting it out) but with it the whole chain succeeds with the recovered value

let promise_array = [];

let p = somePromisyFunction1()
//.then(() => { // Do something good })
// .catch(() => { // Handle the bad });
promise_array.push(p);

p = somePromisyFunction2()
//.then(() => { // Do something good })
.catch((v) => "2 - " + v);
promise_array.push(p);

p = somePromisyFunction3()
//.then(() => { // Do something good })
//.catch(() => { // Handle the bad });
promise_array.push(p);

// ...
// More Promisy functions added to promise_array here...
// ...

Promise.all(promise_array)
.then((v) => { console.log("all good",v) })
.catch(() => { console.log("something bad") });


function somePromisyFunction1(){
return new Promise(resolve => resolve("1"));
}

function somePromisyFunction2(){
return new Promise((resolve,reject) => reject("oh no something went wrong!"));
}

function somePromisyFunction3(){
return new Promise(resolve => resolve("3"));
}

Why Promise.all doesn't reject when a non-promise throws an error?

Because the error is thrown before you call Promise.all. It's impossible for Promise.all to convert that to rejection for you.

You're calling promise2 when building the array, which happens before you pass that array to Promise.all.

Your code here:

Promise.all([promise1, promise2(), promise3])/*...*/

...is equivalent to the following (other than the variables):

const x0 = promise1;
const x1 = promise2(); // <==== Error is thrown here
const x2 = promise3;
const array = [x0, x1, x2];
Promise.all(array)/*...*/

To make it work as you want, you have several options:

You could make it an async function, as TKoL pointed out in a comment:

const promise1 = Promise.resolve(3);
const promise2 = async () => {throw new Error('random error')};
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2(), promise3]).then((values) => {
console.log(values);
}).catch(e=>console.log('Error caught',e));

How to handle errors using `.catch` in promises

My guess would be that you're not handling errors thrown by Promise.all(), which will be rejected if any of the promises it's handling will be rejected.

So you need to catch errors there as well:

Promise.all(…).then(…).catch(e => …)

FWIW, your catch()'s are superfluous:

.catch(err => throw err)

You're catching errors that were thrown and rethrowing them, which isn't necessary (they will be caught by .catch() that you'll attach to Promise.all()).

Promise data and exception handling

for this kind of situation, you can combine promises and async-await together.

From the question, it seems we have three promises and one function that executes and handle them.

You can try something like this -

const subProcess = () => {
return new Promise((resolve, reject) => {
// Using IIFE ( You shouldn't put async keyword on promise callbac )
(async () => {
// Use of try catch to handle the errors
try {
await promiseOne()
await promiseTwo()
await promiseThree()
// Additional code if need after them
} catch(err){
// Handle error ( all three promise error will be transferred here )
}
})()

})
}

The above code waits for the promises to execute one by one and also catch error from all three promises if any.

And as @samuei mentioned, you can also use Promise.all() in this.

const subProcess = () => {
return new Promise((resolve, reject) => {
// Using IIFE ( You shouldn't put async keyword on promise callbac )
(async () => {
// Use of try catch to handle the errors
try {
const myPromises = [promiseOne, promiseTwo, promiseThree];
const res = await Promise.all(myPromises);
// Additional code if need after them
} catch(err){
// Handle error ( all three promise error will be transferred here )
}
})()

})
}

And if you don't want to use async-await then you can do something like this as well

const subProcess = () => {
return new Promise((resolve, reject) => {
const myPromises = [];
const myPromises = [promiseOne, promiseTwo, promiseThree];

Promise.all(myPromises)
.then(res => {
// Handle the response
})
.catch(err => {
// Handle the error
})

})
}

Why does Promise.all() throw an exception even if I .catch() it?

As pointed out by Bergi in the comments...

If you execute your first bit of code without the catch statement (or actually print the errors you catch) you'll see what is happening.

Promise.all(new Promise((res, rej) => rej('Failure!')))

Returns:

Promise {<rejected>: TypeError}
Uncaught (in promise) Failure!
Uncaught (in promise) TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
at Function.all (<anonymous>)
at <anonymous>:1:9

Notice the first error is the error we throw by rejecting the promise.

The second error comes from not using Promise.all() correctly, which is the error you were catching.

The error we throw by rejecting the promise is never caught because of the incorrect usage of the Promise.all() method.


Now, let's test out the code with our promise inside an array, which is the correct usage of Promise.all(), as pointed out by Barmar in the comments.

Promise.all([new Promise((res, rej) => rej('Failure!'))])
.catch(() => console.log("It's all okay."))

Returns:

Promise {<fulfilled>}
It´s all okay.

So, we are successfully catching the rejected promise error.

Also worth noting, the final promise returned is the promise that the catch() method returns after execution. Which is fulfilled since we successfully executed the catch statement, although the promise that Promise.all() actually returns is rejected.



Related Topics



Leave a reply



Submit