A scroll-driven explainer · JavaScript
JavaScript has one thread. So how does it wait for timers, network and clicks without freezing? Scroll to run the code, one step at a time, and watch every function travel through the stack and the queues.
Recap
Everything above follows from these. Once they click, you can predict the output of any snippet that mixes timers, promises and await.
A running function is never interrupted. While the stack is busy, nothing else happens: no callback, no click handler, no paint.
The loop takes the oldest task (a timer, an event, a message) and runs it until the stack is empty again.
After each task, the whole microtask queue is drained, including microtasks queued along the way. Promises and await live here.
If a frame is due, the browser runs requestAnimationFrame callbacks, then style, layout and paint. Then back to rule 02.
while (true) { const task = taskQueue.shift(); // rule 02: the oldest task run(task); // rule 01: until the stack is empty while (microtaskQueue.length) run(microtaskQueue.shift()); // rule 03 if (frameIsDue()) { runAnimationFrames(); styleLayoutPaint(); } // rule 04 }
Try it
This ball is moved by JavaScript, one requestAnimationFrame at a time. The bars below it are the time between two painted frames. Each button keeps the main thread busy for 1.5 seconds, in a different way.