This question already has an answer here:
-
Chained promises not passing on rejection
3 answers
I'm having trouble with chaining .then()
calls for a promise. When executing the following code:
var prom = new Promise(function(resolve, reject) {
//let's always fail
reject(Error("buuuu!"));
});
var thenable =
prom.then(
function(done) {
console.log("First handler: Done!: Argument: ", done);
return "First then: DONE";
},
function(fail) {
console.error("First handler: Fail!. Argument: ", fail);
return "First then: FAIL";
}
).then(
function(done) {
console.info("Second handler: Done!. Argument: ", done);
},
function(fail) {
console.error("Second handler: Fail!. Argument: ", fail);
}
);
This prints the following in the console:
First handler: Fail!. Argument: Error {stack: (...), message: "buuuu!"}
Second handler: Done!. Argument: First then: FAIL
Why does the second then()
has its done
handler called instead of the fail
one?
Is this a bug with Chrome? (Note that I'm only interested in Google Chrome's behavior)
Do I need to resort to returning pre-resolved/rejected Promises from the .then
handlers?
Why does the second then() has its done handler called instead of the fail one?
Because you handled the error in the first then()
of the chain already, making the promise resolve with the "First then: FAIL"
string that you returned from it.
Is this a bug with Chrome?
No, that's how promises are supposed to work.
Do I need to resort to returning pre-resolved/rejected Promises from the .then handlers?
You can do that, yes. Other ways to trigger the second fail handler would be:
Simply omit the first error handler:
prom.then(function(done) {
console.log("First handler: Done!: Argument: ", done);
return "First then: DONE";
}).then(function(done) {
console.info("Second handler: Done!. Argument: ", done);
}, function(fail) {
console.error("Second handler: Fail!. Argument: ", fail);
});
Or rethrow an exception (that's similar to returning a rejected promise):
prom.then(function(done) {
console.log("First handler: Done!: Argument: ", done);
return "First then: DONE";
}, function(fail) {
console.error("First handler: Fail!. Argument: ", fail);
throw new Error("First then: FAIL"); // or: throw fail;
// alternatively, you can return a rejected promise:
return Promise.reject(new Error("First then: FAIL"));
}).then(function(done) {
console.info("Second handler: Done!. Argument: ", done);
}, function(fail) {
console.error("Second handler: Fail!. Argument: ", fail);
});
Why does the second then() has its done handler called instead of the fail one?
Let's look at how your code looks in the synchronous world:
var value;
try{
try {
throw new Error("buuu!");
console.log("First handler: Done!: Argument: ");
value = "First then: DONE";
} catch(e){
console.error("First handler: Fail!. Argument: ", e);
value = "First then: FAIL";
}
console.info("Second handler: Done!. Argument: ", value);
}catch(e){
console.error("Second handler: Fail!. Argument: ", e);
}
When you handle an error in a catch
clause, just like when you handle an error in a then
clause, you recover from it. The promises example is exactly like our synchronous one.
If you want to signal that you performed your recovery logic but are still in a rejected state on the chain, rethrow. That's how it's always done in synchronous code.