(async function() {
var a,b;
function flush(){
return new Promise(res => {
res(123)
})}
Promise.resolve().then(() => a = 1)
Promise.resolve().then(() => b = 2)
await flush();
console.log(a)
console.log(b)
})()
In this snippet, the value of a
and b
gets logged in the console.
(async function() {
var a;
var b;
function flush(){
return new Promise(res => {
res(123)
})}
Promise.resolve().then(() => a = 1).then(() => b = 2)
await flush();
console.log(a)
console.log(b)
})()
In this case the value of a
gets logged as 1, whereas b
is undefined.
(async function() {
var a;
var b;
function flush(){
return new Promise(res => {
setTimeout(res)
})}
Promise.resolve().then(() => a = 1).then(() => b = 2)
await flush();
console.log(a)
console.log(b)
})()
This gives the same result as the first snippet, with the value a
as 1 and b
as 2
I would like to understand, why does promise chaining behaves differently than multiple individual promises
PS: I have a basic understanding of microtask queueing and the event loop.
Running Node 12.3.1, I can reproduce the observation stated in the question,
after changing setTimeout(res(123))
to setTimeout(() => res(123))
.
In JavaScript the concurrency model is the event loop, in which the single thread executes callbacks from a queue.
In the first snippet, the following happens.
.then
adds the callback () => a = 1
to the queue.() => b = 2
is added to the queue.() => console.log(a); console.log(b)
2 is added to the queue.a
is set to 1b
is set to 2a
and b
logged.Since setting the variables happens before printing them, you see both 1 and 2.
In the second snippet:
() => a = 1
is added to the queue by .then
.then
returns a new promise, which is not resolved, as the first callback is not run yet. Then second .then
attaches () => b = 2
to the pending promise.() => console.log(a); console.log(b)
is added to the queue.() => a = 1
is run, and fulfills the promise created in step 2. That causes () => b = 2
to be added to the queue.a
and b
logged.b = 2
is run, but this happens after b
, which was undefined
, got printed.In Firefox however, the output of all three snippets are the same.
I managed to produce the above behaviour by adding an async
.
Promise.resolve().then(async () => a = 1).then(() => b = 2)
Here is a simplified that exhibits the same problem. 1 5 2 3 4 in Node but 1 2 3 5 4 in Firefox.
(async function() {
Promise.resolve()
.then(() => console.log(1))
.then(() => console.log(2))
.then(() => console.log(3))
.then(() => console.log(4))
await Promise.resolve()
console.log(5)
})()
But if you change the await
to .then
,
Promise.resolve().then(() => console.log(5))
You get 1 5 2 3 4 in both platforms.3
Why? I googled and found this: https://v8.dev/blog/fast-async
Node 12 optimizes away some extra steps with await
, which previously required an extra throwaway promise, and two more microticks. That seems to be the reason "5" comes two steps earlier in Node 12.
await
turns the rest of the code into a callback..then
and await
are different after all.