1

While messing with JavaScript Promises, I noticed a strange thing: if to return a new Promise inside a callback of a then-statement, such a then-statement seems to pass to the next "then" NOT this new Promise, but the result of the latter being resolved! An example:

new Promise(resolve => { resolve() })
.then(() => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(1);
        }, 1000);
    })
})
.then(x => {
    console.log(typeof x, x);
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(2);
        }, 1000);
    })
})
.then(x => {
    console.log(typeof x, x);
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(3);
        }, 1000);
    })
})
.then(x => {
    console.log(typeof x, x);
})

What I get in the console after this, is:

number 1
number 2
number 3

, which is definitely NOT what I expected, as I know that a then-statement's callbacks should accept a return value of a previous then statement's callback – so I expected to see in the console objects (Promises) instead of numbers... Did I miss something?

Roman Karagodin
  • 740
  • 2
  • 11
  • 16
  • 1
    _"If a handler function: ... 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 resolved value of the promise returned by `then` will be the same as the resolved value of the promise returned by the handler."_ (Source: [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then#Return_value)) – Andreas Jan 02 '21 at 13:15
  • 1
    To rephrase the blurb Andreas posted from MDN promises cannot **nest**, they automatically flatten. Promise(Promise(foo)) is the same as just Promise(foo). This was actually a fairly controversial decision at the time with a lengthy [github thread](https://github.com/promises-aplus/promises-spec/issues/94) argument about monads, but the people driving the A+ spec pushed for and got the current behavior. – Jared Smith Jan 02 '21 at 13:25
  • This is the [central point of promises](https://stackoverflow.com/a/22562045/1048572). Only chaining makes them so useful. – Bergi Jan 02 '21 at 13:29
  • 1
    @JaredSmith It's still controversial :-) And noone on that thread argued that `then` shouldn't unnest, algebraic structure supporters argue that it shouldn't unnest *recursively*. – Bergi Jan 02 '21 at 13:30
  • @Bergi yes I remember now that you were one of the people specifically in that thread right? – Jared Smith Jan 02 '21 at 15:34
  • @Bergi, what is the promise returned by then() and what is the promise returned by the handler? I don't understand the in the text "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 resolved value of the promise returned by then will be the same as the resolved value of the promise returned by the handler. " –  Jun 16 '22 at 01:21
  • @Andreas, I don't know who can answer my question, please. –  Jun 16 '22 at 01:21
  • 1
    @DanielTinajeroDíaz In `a = somePromise.then(() => { …; return b; })`, `a` is the promise returned by `.then()` whereas `b` is the promise returned by the `then` handler. – Bergi Jun 16 '22 at 11:03
  • great @Bergi, thank you very much, now I understand MDN's statement. –  Jun 16 '22 at 11:19
  • hi @Bergi +1 - "*In `a = somePromise.then(() => { …; return b; })`, `a` is the promise returned by `.then()` whereas `b` is the promise returned by the `then` handler*" - so, the promise `a` is **fulfilled** with the same value as promise `b` or **rejected** with the same reason as `b`, right? –  Mar 01 '23 at 15:15
  • 1
    @Coder23 yes, see the answer below – Bergi Mar 01 '23 at 16:23
  • thanks @Bergi +1, yes I saw them, but as always it is always good to know an opinion **more** from a referent in promises. –  Mar 01 '23 at 17:27
  • in fact, all the answers are great, they already have all 3 of them my +1 –  Mar 01 '23 at 17:36

3 Answers3

2

When you return something from a callback function of .then() method, if that return value is a non-promise value, then it is implicitly wrapped in a promise and then the promise returned by that .then() method is fulfilled with that non-promise value.

On the other hand, if the return value of the callback function is itself a promise, then the promise returned by the .then() method gets resolved to the promise returned by the callback function. This means that the fate of the promise returned by the .then() method now depends on whatever happens to the promise returned by the callback function.

Example: Assume that the promise returned by the .then() method is promiseThen and the promise returned by the callback function is promiseCallback. Now, if the promiseCallback is fulfilled, then the promiseThen will also fulfill with the value with which promiseCallback fulfilled and this value is what will be passed to the next .then() method, if there is one. Similarly, if promiseCallback is rejected, then promiseThen will also get rejected.

Yousaf
  • 27,861
  • 6
  • 44
  • 69
  • thanks +1 @Yousaf - the example (the last paragraph) of the best I have seen in StackOverFlow of this case –  Mar 01 '23 at 17:37
2

Promise.prototype.then()

The then() method returns a Promise. It takes up to two arguments: callback functions for the success and failure cases of the Promise.

...

Return value

Once a Promise is fulfilled or rejected, the respective handler function (onFulfilled or onRejected) will be called asynchronously (scheduled in the current thread loop). The behavior of the handler function follows a specific set of rules. If a handler function:

  • returns a value, the promise returned by then gets resolved with the returned value as its value.
  • doesn't return anything, the promise returned by then gets resolved with an undefined value.
  • throws an error, the promise returned by then gets rejected with the thrown error as its value.
  • returns an already fulfilled promise, the promise returned by then gets fulfilled with that promise's value as its value.
  • returns an already rejected promise, the promise returned by then gets rejected with that promise's value as its value.
  • 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 resolved value of the promise returned by then will be the same as the resolved value of the promise returned by the handler.

Source: MDN

The last (bold) bullet point is the answer to your question

Andreas
  • 21,535
  • 7
  • 47
  • 56
2

In your code, there is a promise chain. Please refer https://javascript.info/promise-chaining

In a promise chain, execution of promise then callbacks takes place one after another.

i.e Second promise callback is not executed until the first promise callback execution is completed(first promise enters resolved state). Similarly it goes down till the last promise callback in the execution chain.

If any one of the promise callbacks in a promise chain is failed (promise enters rejected state), then the promise callback execution stops there.

i.e. Second promise callback will not be executed if first promise callback fails

To answer your question

This article gives an in-depth view of Promise similar to your question https://codeburst.io/playing-with-javascript-promises-a-comprehensive-approach-25ab752c78c3

You returned a promise object inside the first then callback. So, the next then (second then) callback will not be executed until the promise returned by first then callback enters resolved state or completes execution

Anand Raj
  • 435
  • 2
  • 8
  • Promises are not executed. Functions (that return promises) can be. – Bergi Jan 02 '21 at 14:17
  • @Bergi It is for easy understanding. I can't find a good word other than execute to describe. Eg: Promises are executed asynchronously – Anand Raj Jan 02 '21 at 14:25
  • But that's wrong. Just be precise about what is actually executed: "*Promise `then` callbacks are executed asynchronously*". – Bergi Jan 02 '21 at 14:26
  • This answer is much more helpful and easier to understand than the excerpt on mdn mentioned in the topmost voted answer. – Chinmay Ghule Nov 16 '22 at 09:11
  • thanks @Anand Raj +1 - *If any one of the promise callbacks in a promise chain is failed (promise enters rejected state), then the **promise callback execution stops there**.* this is worth gold –  Mar 01 '23 at 17:37