Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will reject() skip all following then() in Promise

I am always curious that if a Promise is rejected at any position, will the following then() still be executed? Take the codes below as an example:

Promise.reject('reJECTed')
.then(() => {
    console.log('before resolve()');
    return Promise.resolve('reSOLVed');
})
.then((msg) => {
    console.log('reSOLVed inside 1st then()');
    console.log(msg);
}, (msg) => {
    console.log('reJECTed inside 1st then()');
    console.log(msg);
})
.then((msg) => {
    console.log('reSOLVing inside 2nd then()');
    console.log(msg);
}, (msg) => {
    console.log('reJECTing inside 2nd then()');
    console.log(msg);
})
.catch((msg) => {
    console.log('reJECTed in catch()');
    console.log(msg);
});

It will print

reJECTed inside 1st then()
reJECTed
reSOLVing inside 2nd then()
undefined

on the console, which means the resolve() in second then() and the last catch() were not executed. Does that mean when met reject(), any following resolve()s inside a then() is totally skipped until the rejection is caught?

Thanks for any explanation!

like image 940
Yizhe Avatar asked Mar 03 '17 16:03

Yizhe


2 Answers

Does that mean when met reject(), any following resolve()s inside a then() is totally skipped until the rejection is caught?

Yes. When a promise rejects, all resolve handlers in the chain are skipped up until some reject handler handles the rejection and changes the promise chain back to fulfilled.

And, in case you didn't realize, the 2nd argument to a .then() handler is a reject handler (pretty much the same as a .catch() handler).

But, keep in mind that if you have a reject handler and it does not throw or return a rejected promise, then the chain becomes fulfilled again since you have "handled" the rejection. It's exactly like with try/catch. If you catch and don't rethrow, then the exception is handled and normal execution continues after that.

So, in your reject handler that outputs reJECTed inside 1st then(), you are not returning anything so at that point the promise chain becomes fulfilled. The rejection has been "handled" at that point and the promise chain now switches to the fulfilled state.

Here's some annotation on your code:

Promise.reject('reJECTed')
.then(() => {
    // this fulfilled handler is skipped because the promise chain is rejected here
    console.log('before resolve()');
    return Promise.resolve('reSOLVed');
})
.then((msg) => {
    // this fulfilled handler is skipped because the promise chain is rejected here
    console.log('reSOLVed inside 1st then()');
    console.log(msg);
}, (msg) => {
    // this reject handler is called because the promise chain is rejected here
    console.log('reJECTed inside 1st then()');
    console.log(msg);
    // because this does not rethrow or return a rejected promise
    // the promise chain switches to fulfilled
})
.then((msg) => {
    // this fulfilled handler is called because the promise chain is fulfilled now
    console.log('reSOLVing inside 2nd then()');
    console.log(msg);
}, (msg) => {
    // this reject handler is not called because the promise chain is fulfilled now
    console.log('reJECTing inside 2nd then()');
    console.log(msg);
})
.catch((msg) => {
    // this reject handler is not called because the promise chain is fulfilled now
    console.log('reJECTed in catch()');
    console.log(msg);
});

Let me offer another example to show how a promise chain can switch states:

Promise.reject("Hello").then(val => {
    console.log("1: I am not called");
}).catch(err => {
    console.log("2: I am rejected");
    // rethrow to keep promise chain rejected
    throw err;
}).catch(err => {
    console.log("3: I am still rejected");
    // return normal value, promise chain switches to fulfilled
    return "GoodBye";
}).then(val => {
    console.log("4: Now back to fulfilled state");
}).catch(err => {
    console.log("5: Not called");
});
like image 142
jfriend00 Avatar answered Oct 31 '22 04:10

jfriend00


Yes, this is described in Scenario B


Promise.reject will result in a rejected promise, which means

Scenario A

results in unhandled promise, if no then or catch

Scenario B

it will fallback to the first catch or error hook specified by then

Promise.reject()
      .then(
       () => {},
       () => { console.log ('Im called') } // goes here
      )

    Promise.reject()
      .catch(() => { console.log('Im called as well') }) // or here

Remember that then and catch return either

  • A promise which resolved to their return value
  • Or throw an error / rejected promise

Now everything goes carries on again from Scenario A or B, for instance

Promise.reject()
      .catch(() => { return 5; }) // returns promise that resolves to 5, 
      .then(
        res => console.log('See, Im not rejected and resolve to', 5),
        () => {}
      );
like image 33
Lyubomir Avatar answered Oct 31 '22 04:10

Lyubomir