Software / Browser
The event loop
One thread. The stack runs to empty, then microtasks, then one task, then the browser may paint.
JavaScript Event Loop
JS is single-threaded with a host-managed concurrency model. I/O and timers are offloaded to Web APIs; callbacks are enqueued when ready. The loop's priority ordering is what makes async code predictable.
Microtask Queue
Drained completely after every task and after the initial script. No rAF or paint occurs until the microtask queue is empty. Starvation risk: a microtask that enqueues itself loops forever, blocking all I/O and rendering.
- Promise.then
- queueMicrotask
- MutationObserver
- async/await
Task Queue (Macrotasks)
One task dequeued per loop iteration. Multiple distinct task sources exist (timer, I/O, UI events); the UA may interleave them. MessageChannel yields a macrotask without the 4 ms minimum clamp of setTimeout.
- setTimeout
- setInterval
- MessageChannel
- I/O callbacks
requestAnimationFrame
Fires immediately before the browser paints — ideal for all visual mutations. Batched per frame (~16 ms at 60 fps). Pauses when the tab is hidden. Use to batch DOM reads before writes and avoid layout thrashing.
- requestAnimationFrame
- requestIdleCallback
- frame budget
Scheduler API
scheduler.postTask() gives explicit priority control: user-blocking, user-visible, background. scheduler.yield() cooperatively yields a long task back to the event loop without relinquishing priority.
- scheduler.postTask
- scheduler.yield
- TaskController
- Long Tasks API