Code:
setTimeout(() => console.log("1"), 1000)
const startTime = Date.now()
while (Date.now() - startTime < 5000);
setTimeout(() => console.log("2"), 0)
setTimeout(() => console.log("3"), 0)
The output of this sample is:
2
3
1
Why it works this way ?
In this example the loop blocks the thread for 5 seconds or so. At this moment first setTimeout should have been finished and go out of the macrotask queue first, but it doesn’t.
2
Answers
On your computer 10,000 * 10,000 is taking less than 5 seconds.
This appears to be a side effect of
setTimeout
optimisation in Chromium, but I can’t find documentation that specifically would make this a bug. Quite the opposite,setTimeout
does not guarantee execution order.If you change the timeouts to 1ms it works as you expect:
It looks like, in Chromium at least,
setTimeout
has an optimisation on 0ms to skip checking for timed out actions, and instead just runs them on the next loop (likerequestAnimationFrame
would) before checking for any other expired timeouts.Arguably this is desirable behaviour when using a 0ms timeout, and I don’t think it’s a bug because there’s no guarantees on the timeouts – that’s in the spec.
I’d recommend using
requestAnimationFrame
oversetTimeout(..., 0)
for consistency.Original answer….
Your problem is that this:
Is not a slow operation. Most JS engines can optimise loops and additions like this.
You can use
await
to stall your execution and come back:But, that doesn’t block the JS – you’ll get
1
after1000ms
and then wait 4 more seconds for2
and3
.If you want to block the executing JS that’s harder, as they’ve worked very hard to remove blocking actions. For instance
fetch
doesn’t support it, and whileXMLHttpRequest
has anasync=false
flag you could use to block while downloading something large, I think some browsers ignore that now.