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
What's the Significant Use of Unary Plus and Minus Operators
How to Fire and Forget a Promise in Nodejs (Es7)
Why Does JavaScript Handle the Plus and Minus Operators Between Strings and Numbers Differently
Array.Fill(Array) Creates Copies by References Not by Value
Pass Props to Parent Component in React.Js
Create a Custom Callback in JavaScript
Allow Google Chrome to Use Xmlhttprequest to Load a Url from a Local File
How to Check If a Checkbox Is Checked
How to Access Iframe Elements with JavaScript
Prevent Execution of Parent Event Handler
Sort Array by Firstname (Alphabetically) in JavaScript
Defining a JavaScript Prototype
Jquery's Jquery-1.10.2.Min.Map Is Triggering a 404 (Not Found)
How Does Facebook Disable the Browser's Integrated Developer Tools
Why Was the Arguments.Callee.Caller Property Deprecated in JavaScript