Consider this:
const a = () => {
return new Promise((resolve) => {
resolve(1);
});
};
const b = () => {
return new Promise((resolve) => {
a()
.then((aR) => {
console.log(aR);
resolve(2);
})
.finally(() => {
console.log("A");
});
});
};
const c = () => {
b().then((bR) => {
console.log(bR);
});
};
c();
Why it prints 1 2 A
, not 1 A 2
?
The problem is originally discovered by @rebuilding127 on Segmentfault, a StackOverflow-like Chinese forum. I think it’s worth translating & publishing here.
You can see this chain-call:
a()
.then((aR) => {
console.log(aR);
resolve(2);
})
.finally(() => {
console.log("A");
});
This is a chained invocation, where a().then(...)
returns a Promise
, it will output A
after the returned Promise
is fulfilled, not after a()
is fulfilled. So resolve(2)
will enqueue b().then
before fulfilling a().then
and enqueuing a().finally
.
To make it clearer, I rewrited the code:
const f = () => {
const a = new Promise((resolve) => {
const cb1 = (result1) => {
console.log(result1);
resolve(2);
};
const cbA = () => console.log('A');
const b = Promise.resolve(1);
const c = b.then(cb1);
c.finally(cbA);
});
return a;
};
const cb2 = (result2) => console.log(result2);
f().then(cb2);
The execution unfolds like this:
- Start executing
f
- Start executing
a
b
is immediately fulfilled, causingcb1
to be enqueued into the microtask queue (now queue =[cb1]
)c
‘s executor enqueuesf
returns- Start executing
cb1
from the queue (now queue =[]
) - Outputs
1
, resolvesa
, causingcb2
to be enqueued (now queue =[cb2]
) cb1
returns, causingc
to be resolved, thencbA
to be enqueued (now queue =[cb2, cbA]
)- Start executing
cb2
from the queue (now queue =[cbA]
) - Outputs
2
- Start executing
cbA
from the queue; now queue =[]
. - Outputs
A
, tick ends
Also published under the original problem on SegmentFault.