Reactgular
Reactgular

Reputation: 54771

Why do JavaScript promises chain rejections into resolved promises?

I'm wondering why rejections are chained as resolved with ES6 promises?

Here's an example:

let p = Promise.reject().then(function () {
    return 'one';
}, function() {
    return 'two';
});

p.then(function (value) {
   // I do not expect this to be executed, since it was not resolved.
   console.log(value);
});

The above outputs "two" to the console.

https://jsfiddle.net/pscb88xg/1/

Why does the chaining of a promise mutate a rejection into a successful resolve?

Edit: I want to clarify that the question has practical application.

What if you want to convert data from A to B using chaining.

 p.then(function (A) {
     return new B(A);
 });

The above mutates rejections into resolved values. Even if no reject callback is used.

For example;

 let p = Promise.reject('error').then(function (A) {
      return new B(A);
 });

 // some code elsewhere

 p.then(function (B) {
      console.log('success!');
 });

In the above example. The value B is not B but the error, and it was resolved successfully later in the chain.

Is this normal?

Edit: I understand my confusion now. I was extracting HTTP header values in rejections like this.

 let p = myHttpService.get(...).then(function() {
          //....
 }, function(response) {
          // get headers
 });

The above was chaining my promises to a resolved value, and I didn't understand why. I can fix my code with the following.

 let p = myHttpService.get(...).then(function() {
          //....
 }, function(response) {
          // get headers
          return Promise.reject(response);
 });

Upvotes: 2

Views: 744

Answers (4)

toskv
toskv

Reputation: 31600

It's meant to allow you to gracefully recover from an error in a promise chain.

An example might be the 304 Not Modified response from the server. If you were to use a promise based library to do an http request any response that's not 2XX will be considered a failure and the promise will be rejected. From an application's point of view however 304 might just as good as a 200 and you'd like to continue as normal.

Upvotes: 1

Pop-A-Stash
Pop-A-Stash

Reputation: 6652

This is the same behavior as AngularJS's $q provider.

The mutation occurs because in your rejection handler, you are returning a value and not a rejected promise. If you were to instead, pass a rejected promise, it would behave how you were expecting:

let p = Promise.reject().then(function () {
    return 'one';
}, function() {
    return Promise.reject('two');
});

p.then(function (value) {
   // I do not expect this to be executed, since it was not resolved.
   console.log(value);
}, function() {
  console.log("Rejected, baby!");
});

Upvotes: 0

Michał Perłakowski
Michał Perłakowski

Reputation: 92521

MDN documentation for Promise.prototype.then says:

After the invocation of the handler function [the function passed to then()], the promise returned by then gets resolved with the returned value as its value.

Upvotes: 1

Paul
Paul

Reputation: 141827

After handling an error you usually want your code to continue, similar to how code after a catch block runs like normal, whereas uncaught exceptions abort.

If you want to abort instead then don't handle the error until the end of the chain:

let p = Promise.reject().then(function () {
    return 'one';
});

p.then(function (value) {
   // This won't run, because the rejection hasn't been handled yet
   console.log(value);
}, function() {
   return console.log( 'there was a problem' );
}).then(function ( ) {
   // This will run because the rejection has been dealt with already.
   console.log( 'Moving on');
});

Upvotes: 1

Related Questions