skip to Main Content

Below code snippet I have taken from the famous YDKJS – Async & Performance series, and have made a few changes for the sake of understanding.

var p3 = new Promise( function(resolve,reject){
    resolve( "B" );
} );
var p1 = new Promise( function(res,rej){
  resolve( p3 ); // equivalent to p3.then(res)
} );
var p2 = new Promise( function(resolve,reject){
    resolve( "A" );
} );
p1.then( function(v){
    console.log("In p1");
    console.log(p1);
} );
p2.then( function(v){
    console.log("In p2");
    console.log(p1);
} ); 

Although the order in which the results are logged are as per desire, but the state of promise p1 shows pending (at console.log(p1)) which I could not understand. I have tried to depict through pictures (at the end) what I have understood so far, so you can correct me where I’m wrong.

Result

enter image description here

At Stage 1: p1 resolves to an already resolved promise, p3. I know p1 would still remain pending and would not resolve to a value yet. But I have heard that resolve(p3) is equivalent to

p3.then(res);  // res is a resolve callback of p1

So based on this assumption, since p3 is both resolved and has a registered handler (callback res of p1) it will be inserted into Micro Task Queue (MTQ).

At Stage 2: p2 is both resolved and has a registered handler (in purple) it will be appended to MTQ.

At Stage 3: Now as there’s left nothing on stack to execute, the cb (in yellow) which was first inserted in MTQ will get on stack for execution.
Here are my Queries: When res('B') executes will it mark the p1‘s state as Fulfilled? Since res is the callback associated with p1.

As p1 already has a registered handler (when p1.then was called) besides being fulfilled, wouldn’t it append to the MTQ as depicted at Stage 4?

If so, then why is p1 still showing as pending even at Stage 5?

And at Stage 6: How does p1 gets fulfilled all of a sudden?

Please assist me to understand where I’m wrong in below pictorial depiction?

enter image description here

2

Answers


  1. Why promise state shows pending, when it has resolved to another promise?

    That’s just terminology. The implementation does not distinguish between the "unresolved" and "resolved" pending states.

    When res('B') executes will it mark the p1‘s state as Fulfilled?

    Yes.

    Wouldn’t the callback registered on p1 be appended to the MTQ as depicted at Stage 4? Why is p1 still showing as pending even at Stage 5?

    Your reasoning is mostly correct. Yes, it would. In fact, if you had written p3.then(resolve) in your p1 execution callback, that’s exactly what would have happened and the Stage 5 console.log would have printed Promise {<fulfilled>: ‘B’}.

    I have heard that resolve(p3) is equivalent to p3.then(resolve);. So based on this assumption…

    That assumption is not exactly valid. It is indeed mostly equivalent in behaviour, but it is not equivalent in microtask timing. When you pass a thenable (a promise) to resolve, it will actually schedule the call to the .then(resolve, reject) in a new microtask instead of synchronously calling it from within resolve().

      • a: p3 is created, p3 is fulfilled with "B"
      • b: p1 is created, resolve(p3) accesses p3.then, finds it to be a method, and schedules a callback to invoke it
      • c: p2 is created, p2 is fulfilled with "A"
      • d: p1.then(…) registers a callback on the promise
      • e: p2.then(…) sees that p2 is already fulfilled and schedules the callback
    1. The task from step 1b runs and calls p3.then(resolve, reject) to adopt its state to p1. It sees that p3 is already fulfilled and schedules the callback
    2. The task from step 1e runs:
      • a: it logs "In p2"
      • b: it logs p1, which is still pending (but already resolved to p3)
    3. The task from step 2 runs and calls resolve("B"). This fulfills p1 and schedules the callback that was registered on it.
    4. The task from step 4 runs:
      • a: it logs "In p1"
      • b: it logs p1, which had been fulfilled with "B" in step 4.

    You could read the spec for such intricate details, but really you just shouldn’t rely on them (and they are changing from time to time – in fact the very detail that you missed is proposed to change). You have two independent promise chains, and you should not make any assumptions on the order of callbacks between p1 and p2. If it was important, you’d make the promises dependent on each other.

    Login or Signup to reply.
  2. I can’t top Bergi’s answer, but for a simplistic view, this is how I read the code (in order of execution stages):

      • p3, p1, and p2 are constructed and callbacks are attached to p1 and p3, in order, synchronously
      • (a) p3 is resolved to value B
      • (b) p3.then(() => res()) callback is added to the queue to resolve p1 the next time through the queue
      • (c) p2 is resolved to value A
      • (a) p2‘s then callback is executed as p2 has been resolved
      • (b) p3‘s then callback is executed (whose outcome resolve’s p1)
      • (a) p1‘s then callback is executed, as the execution of p3‘s then callback in step 3b determined p1 should be resolved
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search