Search code examples
javascriptecmascript-6es6-promise

How is a Promise That Is Returned From A Resolve Handler Mapped To The Return Value of 'then'?


If I'm chaining Promises and I return a Promise from the resolve handler, how does that Promise become the return value of the call to then? What is going on behind the scenes?

In the following example, in firstMethodHandler, the call to secondMethodreceives a new Promise which it then returns, but that promise is then returned from the then method in which it is passed to its promise. How does this happen?

function firstMethod(value) {
  console.log('1st method:', value)
  return new Promise((resolve, reject) => {
    resolve(++value);
  });
}

function secondMethod(value) {
  return new Promise((resolve, reject) => {
    console.log('2nd method:', value)
    resolve(++value);
  });
}

function firstMethodHandler(value) {
  console.log("1st method handler:",value);
  return secondMethod(value);
}

function secondMethodHandler(value) {
  console.log("2nd method handler:", value);
}


firstMethod(1)
  .then(firstMethodHandler)
  .then(secondMethodHandler)

Solution

  • The key here is that p1.then() returns a new promise we will call p2. When p1 is resolved, it calls the .then() handler attached to it. When you return another promise from that .then() (which we will call p3), then p3 is chained to p2 and thus p2 will not be resolved until p3 resolves. So, the caller of the original p1.then() will get back a promise p2 that won't be resolved until both p1 and p3 are resolved. That's how things are chained together.

    Usually, the key piece of information here is that p1.then() returns a new promise and it is that promise that is affected by what happens inside the previous .then() handler.

    You can see a similar explanation here:

    Difference between resolve and return in promise JS


    In your specific example:

    firstMethod(1)
      .then(firstMethodHandler)
      .then(secondMethodHandler)
    

    firstMethod() returns a promise I will call p1. Then, calling .then(firstMethodHandler) on that promise returns a new promise p2 and then calling .then(secondMethodHandler) on that creates a new promise p3.

    At some point in the future, firstMethod() resolves the promise it returned. So, now p1 is resolved. That calls the .then() handler attached to it and thus calls firstMethodHandler(). That returns a new promise p4. That chains p4 to the p2 promise so p2 will not resolve until p4 does. At some point in the future, p4 resolves which allows p2 to resolve. That calls the .then() handler attached to p2 which thus calls secondMethodHandler() and you see the final console.log().

    As you can see from the explanation, the key here is the new promises p2 and p3 that are created when .then() is first executed. That's what the chained .then() handlers are actually linked to and those promises are what are influenced by what is returned from the attached .then() handlers.

    To see what the chaining is doing, we can remove the actual chaining and show you the actual intermediate variables that are automatically created and used:

    var p1 = firstMethod(1);
    var p2 = p1.then(firstMethodHandler);
    var p3 = p2.then(secondMethodHandler);
    

    p1 is resolved internal to firstMethod() p2 is the return value of p1.then(...) p3 is the return value of p2.then(...)

    When firstMethodHandler is called (after p1 resolves), then it returns p4 which is chained to p2 so that p2 does not resolved until p4 resolves. When p4 finally resolves, it allows p2 to resolve which then calls secondMethodHandler. When that .then() handler returns a normal value, then p3 is resolved and the whole chain is done.