Chained then in promise

Need some help understanding the Promise object. Consider the following code block:

new Promise(function (resolve, reject) {
  reject(1);
}).then(function (v) {
  console.log("resolved:", v);
  return v;
}, function (v) {
  console.log("rejected:", v);
  return false;
}).then(function (v) {
  console.log("chained then resolved: ", v);
}, function (v) {
  console.log("chained then rejected: ", v);
});

I was expecting the above to produce the following result:

rejected: 1
chained then rejected: false

However, it is producing:

rejected: 1
chained then resolved: false

Why is it invoking the resolved handler of the chained then? Running a similar form with jQuery’s Deferred construct works as intended. What am I missing?

With this you can still do deferred jQuery style. This shows how: https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred

Simply put this is how you get a Deferred:

var deferredDone = {};
deferredDone.promise = new Promise(function(resolve, reject) {
    this.resolve = resolve;
    this.reject = reject;
}.bind(deferredDone));
Object.freeze(deferred);
var promiseDone = deferredDone.promise;

Can you put a .then on a .then? I thought .then is only on promise object?

Also returning values in the resolve/reject of the .then does nothing no? It seems above though the returned value is being passed to the next .then?

Short version: the second .then is being attached to a new promise that is properly handled (not rejected); it is not the original promise.

This will work as you expect it:

var test1 = new Promise(function (resolve, reject) {
  reject(1);
});

var test2 = test1.then(function (v) {
  console.log("resolved:", v);
  return v;
}, function (v) {
  console.log("rejected:", v);
  return false;
});

test1.then(function (v) {
  console.log("test1 resolved: ", v);
}, function (v) {
  console.log("test1 rejected: ", v);
});

test2.then(function (v) {
  console.log("test2 resolved: ", v);
}, function (v) {
  console.log("test2 rejected: ", v);
});

Long version :slight_smile: :


1 Like

The value returned in a .then will be the resolved/rejected value of the promise returned by .then; in the example above, you get “test2 resolved: false” at the end because it is returning false above it. (This is just a typical return value though, I believe there are special return/resolve/reject rules when passing/returning other promises and stuff… The docs I linked above probably have more details on that.)

1 Like

Thanks @quicksilver! I never usually return from the callbacks so this is very informative. (im a deferred resolver, as i do same with angular framework)

Ok. I’ve read up on the topic and can now say I understand why I was having difficulties with chained ES6 Promises.

To keep it short and succint, when using then in the form (success, failure) one has to throw an exception inside the failure handlers so as force the failure to propagate down the promise chain. Not throwing an exception leads the promise engine to assume that the error was absorbed and dealt with, and thus the promise behaves as if it had resolved instead. Specifically, the following works:

new Promise(function (resolve, reject) {
  console.log("rejecting");
  reject(false);
}).then(function (v) {
  console.log("resolved:", v);
  return v;
}, function (v) {
  console.log("rejected:", v);
  throw(v);         // throw!
}).then(function (v) {
  console.log("chained then resolved: ", v);
}, function (v) {
  console.log("chained then rejected: ", v);
  // throw(v);
});

However, this is considered an anti-pattern. The right way to use the ES6 Promise is as given:

new Promise(function (resolve, reject) {
  resolve(1);
}).then(function (v) {
  console.log("first then:", v);
  return new Promise(function (resolve, reject) {
    reject(false);
  });
}).then(function (v) {
  console.log("second then: ", v);
}).then(function (v) {
  console.log("third then: ", v);
}).catch(function (v) {
  console.error("third catch: ", v);
});

Using the form above, the then are given just one handler, which are invoked when the promise resolves, and one must rely on catch to trap promise rejections or errors.

Quite a nice and clean design, actually.

1 Like