2

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);
 });
Reactgular
  • 52,335
  • 19
  • 158
  • 208
  • ... because... that's what it do? that's how it's designed. – Kevin B Jun 02 '17 at 20:08
  • I believe adding the second callback to `.then()` catches rejections – mhodges Jun 02 '17 at 20:11
  • I love how someone has downvoted the question and all the answers. He really hates this thread! – Pop-A-Stash Jun 02 '17 at 20:13
  • your edit... is incorrect. it would result in an unhandled rejection. `// some code elsewhere` must be interacting with it. – Kevin B Jun 02 '17 at 20:16
  • The second half or your question is incorrect. That code would not log 'success'. Perhaps `p` is not what you think it is at that point. – Paul Jun 02 '17 at 20:17
  • Thanks for the down votes. I'll keep trying until I understand even without friendly help. – Reactgular Jun 02 '17 at 20:17
  • the purpose is simply to allow you to transform a reject into a success so you can continue chaining irregardless of the failure. graceful error handling. – Kevin B Jun 02 '17 at 20:18
  • @KevinB so if I provide a callback that makes it **handled** and thus can be resolved later? The key is to not use a rejection callback when you don't want it to mutate the result. – Reactgular Jun 02 '17 at 20:18
  • 1
    For example, say you want to get data from a server. The data isn't necessary to the operation of the page, but you would like to wait until you have the data (or have failed to retrieve the data) before you perform action A. using the .then() error callback would allow you to transform a failure to get data from the server into a success with no data that you can then continue chaining with, meaning none of the logic that comes after it has to care whether the server failed to respond or if it responded successfully with no data. It'l just act upon the data you give it. – Kevin B Jun 02 '17 at 20:21
  • Related: [Why do both Promise's then & catch callbacks get called?](https://stackoverflow.com/q/40069638/3853934) – Michał Perłakowski Jun 02 '17 at 20:24
  • I'm not sure enough about how to explain this, but I think this example covers what you were hoping to achieve: https://jsfiddle.net/v5ukz3L4/1/ – JBC Jun 02 '17 at 20:51

4 Answers4

1

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.

toskv
  • 30,680
  • 7
  • 72
  • 74
1

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');
});
Paul
  • 139,544
  • 27
  • 275
  • 264
1

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.

Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
0

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!");
});
Pop-A-Stash
  • 6,572
  • 5
  • 28
  • 54