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
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?


That's just terminology. The implementation does not distinguish between the "unresolved" and "resolved" pending states.
Yes.
Your reasoning is mostly correct. Yes, it would. In fact, if you had written
p3.then(resolve)in yourp1execution callback, that's exactly what would have happened and the Stage 5console.logwould have printed Promise {<fulfilled>: 'B'}.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 withinresolve().p3is created,p3is fulfilled with"B"p1is created,resolve(p3)accessesp3.then, finds it to be a method, and schedules a callback to invoke itp2is created,p2is fulfilled with"A"p1.then(…)registers a callback on the promisep2.then(…)sees thatp2is already fulfilled and schedules the callbackp3.then(resolve, reject)to adopt its state top1. It sees thatp3is already fulfilled and schedules the callback"In p2"p1, which is still pending (but already resolved top3)resolve("B"). This fulfillsp1and schedules the callback that was registered on it."In p1"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
p1andp2. If it was important, you'd make the promises dependent on each other.