Confuse about error and reject in Promise

2020-08-09 10:28发布

问题:

All:

I am pretty new to JS Promise, there is one confuse when it comes to Promise chaining, say I have a promise chaining like:

var p = new Promise(function(res, rej){
})
.then(
    function(data){
    }, 
    function(err){
    })
.then(
    function(data){
    }, 
    function(err){
    })
.catch(
    function(err){
    })

What confuse me:

  1. When the function(err) get called and when the catch get called?
  2. How to resolve and reject in then?

Thanks

回答1:

The formular for using a Promise is:

var p = new Promise(function(resolve, reject) {

  var condition = doSomething();

  if (condition) {
    resolve(data);
  } else {
    reject(err);
  }

});

There is nothing special about .catch, it is just sugar for .then (undefined, func), but .catch more clearly communicates that it is purely an error handler.

If a Promise does not resolve and no rejection callback is provided in it, it skips forward to the next .then in the chain with a rejection callback in it. The rejection callback is the reject(err).

For more detailed explanations see: Javascript Promises - There and Back again.


That is: in your example the .catch only gets called if the preceding rejection callback has an error in it. That is there is an error in the reject(err) function itself - which has nothing to do with the preceding Promise not resolving.

You can essentially limit yourself to a rejection callback in the .catch at you end of the .then chain. Any Error in any .then will then fall through to the .catch. One subtlety though: any error in the .catch is not caught.



回答2:

The important thing to know is that the .then() method is always chained onto a Promise, and it returns a new Promise whose value and resolved/rejected state is based on what the function given to it returned.

In your example, if the original Promise resolves, then the first function in your first .then() will get called with the resolved value. If it returns a value then whatever value it returns will then get ultimately passed into the first function in your second .then(). The function in catch will never get called.

If the Promise rejects, the second function in your first .then() will get called with the rejected value, and whatever value it returns will become a new resolved Promise which passes into the first function of your second then. Catch is never called here either. It's only if the Promise rejects and you keep returning rejected Promises or throwing errors in both your function(err){} functions that you'll get the function(err){} in your catch block called.

To resolve in your function(data){} functions, all you need to do is return a value (or return a Promise/thenable that later resolves). To reject, you would need to either throw an error, actually cause an error, return a new Promise that eventually rejects, or explicitly return Promise.reject(::some value::).

To resolve in your function(err){} blocks, all you need to do is return a new value. You could also return a Promise, in which case that Promise is what will be returned (eventually resolving or rejecting).

In general, it's not wise to define both the resolved and rejected path in the same .then() though: PROMISE.then(fn).catch(fn) is a much safer/clearer practice, because then any errors in the first .then() will be caught by catch. If you do PROMISE.then(fn, fn) instead though, if an error happens in the first function it would NOT get caught by the second: some later chained on method would have to catch it.



回答3:

  1. Note the example executor function in

    var p = new Promise(function(res, rej){});

    is incomplete. An actual executor function supplied to the Promise constructor must call its first argument (res) to resolve the constructed promise, or its second argument (rej) to reject the promise. These calls are normally made asynchronously but don't have to be in ES6.

  2. When a promise is resolved with a Promise object (or any object with a .then property which is a function) nothing happens until the promise object supplied in resolution itself becomes fulfilled or rejected. Fulfilled values are passed to .then onFulfilled handlers, rejected values are passed to .then onRejected handlers/listeners/callbacks (depending on your terminology).

  3. But when a promise is resolved with a non promise (like) object, listeners supplied as the first parameter to .then are called with the resolution value.

  4. When a promise is rejected with any value, listeners supplied as the second parameter to .then, or first parameter to .catch, are called with the rejected value.

  5. .catch is a euphemism for calling .then with the supplied argument as second parameter and omitting the first parameter, as in

    Promise.prototype.catch = function( listener) { return this.then(null, listener);};

  6. The behavior of .then registered onFulfill and onReject functions is the same. To reject a chained promise throw an error. To fulfill a chained promise return a non promise value. To hold up a chained promise return a promise (or promise like) object.


  7. (Update) When a parameter supplied to .then( onFulfill, onReject) is missing or not a function object, processing is equivalent to supplying a dummy function from:

    function onFulfill( data) { return data;}
    function onReject( err) { throw err;}
    

    This is the usual case when calling then or catch with a single argument.