Promise's second .then() not failing [duplicat

2019-01-20 03:03发布

问题:

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?

回答1:

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);
    });
    


回答2:

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.