Given the following code
Promise.resolve().then(() => {
console.log(0);
return Promise.resolve(4);
}).then((res) => {
console.log(res)
})
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
}).then(() => {
console.log(6);
})
i thought that this would give 0 4 1 2 3 5 6
but the output is 0 1 2 3 4 5 6
.
Can anyone comment as to why this is the case?
CodePudding user response:
First off, this is two independent promise chains. In real world asynchronous coding the timing between different steps in these two independent chains is entirely disconnected. So, this is not a real-world problem at all. It's a purely academic exercise or one of intellectual curiosity.
In real world coding, if you cared about the relative order between the operations in these two chains, you would not have two independent promise chains. Instead, you'd link them into one chain where you can control the order.
Oh, and there can even occasionally be changes to the Javascript/promise spec that might influence this level of detail too. A few years ago, a change in the spec was made to how await
works in order to improve promise performance and that did affect detailed timings in these type of test scenarios (when using await
).
That said, the only real question is why the output for 4
comes where it does. We can initially examine the problem by changing the code to have the first .then()
handler not return Promise.resolve(4)
, but to instead just do return 4;
. When we do that, this is what you get:
Promise.resolve().then(() => {
console.log(0);
return 4;
}).then((res) => {
console.log(res)
})
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
}).then(() => {
console.log(6);
})
And, you get the output:
0
1
4
2
3
5
6
OK, so now the problem is reduced to why does return Promise.resolve(4)
cause 4
to be delayed two spots in the order vs. return 4;
?
This has to do with the difference in returning a promise from a .then()
handler vs. returning a plain value. When you return a value, the next promise in the chain can be immediately resolved with that value and the succeeding .then()
handlers attached to that promise can be immediately added to the promise job queue.
But, when you return a promise, the next promise in the chain has to just monitor that returned promise by adding a .then()
handler to it and waiting for it to get resolved. Even though it is already technically resolved,, it's still the case that the only way you get the value out of the promise is by attached a .then()
handler to it (or using await
) so that still has to happen. This allows other things to get into the promise job queue ahead of it.
So, you can immediately see that the 4
output will be delayed some. This delay allows 2
to be output next. Then, the .then()
handler attached to Promise.resolve(4)
by the parent promise gets to execute, so now the first promise chain gets to begin to move onto the next step. To do that, it adds it's next .then()
handler job in the promise job queue. But, it's not the next job in the queue. The next job in the queue was put there from the 2nd chain after it output 2
. So, we get that job next and the output 3
comes and the finally 4
gets to go after that.
So by doing return Promise.resolve(4)
, we not only delayed this chain by one cycle, but we also allowed the next event in the second promise chain to get scheduled ahead of the next event in the first chain - thus essentially costing us two positions in the job queue.
So, the difference between return Promise.resolve(4)
and return 4
is that the subsequent .then()
handler drops two spots in the relative order here due to the timing of adding .then()
handlers to the promise job queue.