2

I have some squares sliding down the page, in a chained promise fashion: https://jsfiddle.net/u4x0qwfo/3

The code is:

new Promise(function(resolve, reject) {
  $("#shape").css({
    top: 100
  });
  setTimeout(function() {
    resolve();
  }, 1000);
}).then(function() {
  return new Promise(function(resolve, reject) {
    $("#shape2").css({
      top: 100
    });
    setTimeout(function() {
      resolve();
    }, 1000);
  });
}).then(function() {
  return new Promise(function(resolve, reject) {
    $("#shape3").css({
      top: 100
    });
    setTimeout(function() {
      resolve();
    }, 1000);
  });
}).then(function() {
  return new Promise(function(resolve, reject) {
    $("#shape4").css({
      top: 100
    });
    setTimeout(function() {
      resolve();
    }, 1000);
  });
});

(the code doesn't run as well inside of a snippet here: here the first square has already slide down initially).

So to see what promise is returned by the fulfillment handler, and what promise is returned by .then(), I have https://jsfiddle.net/u4x0qwfo/10/

The code is:

let foobar;

let lala = new Promise(function(resolve, reject) {
  $("#shape").css({
    // ...
}).then(function() {
  foobar =  new Promise(function(resolve, reject) {
    // ...
  return foobar;
});

lala.then(function() {
  console.log("checking:", lala, foobar, lala === foobar);
  return new Promise(function(resolve, reject) {

In the debug console, we can see that the promises are different. But why do they have to be different?

Actually inside the docs of .then() return value, it is said that:

[if .then()] returns another pending promise object, the resolution/rejection of the promise returned by then will be subsequent to the resolution/rejection of the promise returned by the handler. Also, the value of the promise returned by then will be the same as the value of the promise returned by the handler.

It suggests that the two promises are different (the one returned by the fulfillment handler and the one returned by .then()). (I can't find the description in the ES6 specs). The question is why? Can't they be the same promise?

The second part says:

Also, the value of the promise returned by then will be the same as the value of the promise returned by the handler.

I thought initially it meant "the promise returned by then will be the same the promise returned by the handler", but only to find out it actually meant: "the resolved value of the promise returned by then will be the same as the resolved value of the promise returned by the handler". (is this the proper way to describe it?).

Inside of Google Chrome, both promises will show the same resolved value of 123456: https://jsfiddle.net/u4x0qwfo/11/

nonopolarity
  • 146,324
  • 131
  • 460
  • 740
  • "*Can't they be the same promise?*" - no, they can't. The one returned by `then()` needs to be created before the callback that will in the future create the other one even runs. If at all - the promise might reject. – Bergi Dec 26 '19 at 14:19

2 Answers2

5

This is because those promises are created at a different time:

new Promise(....).then() returns a promise that is immediately available (as lala), while the callback passed to that then method will only be executed when new Promise(....) resolves, i.e. potentially much later.

When eventually that then callback executes, it has full control on what it returns, so if it decides to return a new promise, then how could that ever be the promise that was already created some time ago by the call of .then()?

Or, to put it differently, if the then callback returns a new promise, why would JavaScript overrule that creation, and inject the already existing promise in there? That would not be desirable.

In your code, if you inspect lala in any of the then callbacks, you will notice it is defined (because by that time all non-callback code has already completed), while the assignment to foobar is still to be performed. They cannot be the same.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • so when `foobar` is returned, some mechanism takes this returned value, and see that it is a promise, and immediate calls `returnedValue.then(fn1, fn2)`, where `fn1` is the fulfillment handler and it resolves `lala`... – nonopolarity Dec 26 '19 at 14:31
  • 1
    You can find the specification of what happens in the [Promises/A+](https://promisesaplus.com/#the-promise-resolution-procedure) reference, in section 2.3. In that text, the "thenable `x`" is your `foobar`, and `promise` is your `lala`. You can also have a look at how libraries implement that particular specification. I have posted my own implementation [here](https://stackoverflow.com/a/42057900/5459839) – trincot Dec 26 '19 at 14:39
1

Very short answer then() returns a promise to enable method chaining, if you tell your fulfillment handler to return a promise a equivalent promise will be provided to the next then(). Which means if you create a promise and return it, .then() will access it.

proof below

const promise1 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve("resolve promise1");
    }, 1000);
});

const promise2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve("resolve promise2");
    }, 2000);
});

promise1.then(() => { return promise2 }).then(value => console.log(value))
//this will return resolve promise 2 after 2 seconds

This means that the .then() call will use the resolve value of the promise returned by the fulfillment handler for promiese1, in this case promise2.

For your case foobar !== lala, since lala is in this case the "first promise" in the chain and foobar the 4th or 5th but when you access lala.then() you are accessing the foobar promise but since it doesn't hold any resolve value you wont notice.

Resolve foobar with a value and access it in lala.then(res=>res....)

fubar
  • 383
  • 2
  • 11
  • It is nice that you explain the OP's code, but I am quite sure the OP knew that. It is also nice that you rewrite their code, but I am quite sure the OP is not asking for that either. Where did you attempt to answer their question? – trincot Dec 26 '19 at 16:07
  • I rewrote the code so I can explain better whats happening if some one else stumbles along – fubar Dec 26 '19 at 16:18
  • But, you did not answer the question. That is like a requirement when you post an answer. – trincot Dec 26 '19 at 16:25
  • Thanks for pointing that out. I actually arrived at a different conclusion, it seems that they are actually the same. – fubar Dec 26 '19 at 17:07
  • 1
    After your update, this statement is wrong: *"then() does not return a promise except..."*. `then()` always returns a promise. This is actually adding to the confusion. – trincot Dec 26 '19 at 17:13