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?
2
Answers
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 yourp1
execution callback, that’s exactly what would have happened and the Stage 5console.log
would 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()
.p3
is created,p3
is fulfilled with"B"
p1
is created,resolve(p3)
accessesp3.then
, finds it to be a method, and schedules a callback to invoke itp2
is created,p2
is fulfilled with"A"
p1.then(…)
registers a callback on the promisep2.then(…)
sees thatp2
is already fulfilled and schedules the callbackp3.then(resolve, reject)
to adopt its state top1
. It sees thatp3
is already fulfilled and schedules the callback"In p2"
p1
, which is still pending (but already resolved top3
)resolve("B")
. This fulfillsp1
and 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
p1
andp2
. If it was important, you’d make the promises dependent on each other.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
, andp2
are constructed and callbacks are attached top1
andp3
, in order, synchronouslyp3
is resolved to valueB
p3.then(() => res())
callback is added to the queue to resolvep1
the next time through the queuep2
is resolved to valueA
p2
‘s then callback is executed as p2 has been resolvedp3
‘s then callback is executed (whose outcome resolve’sp1
)p1
‘s then callback is executed, as the execution ofp3
‘s then callback in step 3b determinedp1
should be resolved