awesome-everything RU
↑ Back to the climb

Browser & Frontend Runtime

The event loop: one thread, three queues

Crux How the browser''''s single thread interleaves tasks, microtasks, and rendering — and why whoever runs longest blocks everyone.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at junior altitude — the surface
◷ 12 min

A button click feels instant. A scroll judders. A modal blocks for 300 ms while JSON parses. The thing that decides which experience the user gets is one piece of machinery older than the modern web: the event loop.

What the event loop does

The browser has one thread that runs your JavaScript. While it is busy, nothing else runs — no clicks, no animations, no rendering. The event loop decides what runs next when the thread frees.

The loop has three players:

  • Task queue (macrotask queue) — discrete jobs: setTimeout callbacks, parsed HTML chunks, fired DOM events, message-channel messages.
  • Microtask queue — promise-resolution callbacks and queueMicrotask jobs. Drains to empty after every task.
  • Rendering steps — style, layout, paint, composite. Run between iterations, but only when the browser thinks a frame is due.
Event-loop budgets and limits
Frame budget at 60 fps
16.67 ms
Long-task threshold
50 ms
Good INP (p75)
≤200 ms
setTimeout(0) clamp after 5 levels
4 ms
React 18 work slice
5 ms
Cross-thread postMessage hop
~1 ms + clone

The barista metaphor

Imagine a one-person coffee shop. Customers put orders on the counter (the task queue). When the barista finishes one drink, she checks for sticky notes on the espresso machine (microtasks — small follow-ups like “add cinnamon”) and does every one before greeting the next customer. If she keeps writing herself sticky notes faster than she finishes them, she never reaches the next customer at all. That failure mode is microtask starvation.

Every animation, keystroke, click, and scroll is a queued task waiting for the thread. Hold the thread for 200 ms and the user sees 200 ms of frozen UI. The fix is not “faster code” — it is “shorter tasks.”

One iteration, step by step

One loop iteration does exactly this:

StepWhat runsWhen skipped
1. Select taskPull one task from a non-empty task queueIf all queues empty, idle
2. Run taskExecute the callback to completionNever skipped if step 1 ran
3. Microtask checkpointDrain the microtask queueMicrotask queue empty
4. Update renderingrAF → style → layout → paint → compositeOnly on frame boundary (~16.67 ms)
5. Loop backReturn to step 1Never

A concrete walk-through

Bea · Browser clicks Submit. Sven · Origin server narrates: “The click queues as a task. The submit handler runs — 4 ms to validate, queues a fetch. The fetch resolves later, schedules a .then() microtask that mutates state. React re-renders — more work on the same thread. Total: 28 ms. Frame budget is 16.67 ms, so the user sees one dropped frame.”

Order the steps

Inside one iteration of the browser's event loop, these steps run in a fixed order. Drag them into the right sequence.

  1. 1 Pull one task from the task queue and run it to completion
  2. 2 Drain the microtask queue (run promise callbacks until empty)
  3. 3 Run requestAnimationFrame callbacks (only if a frame is due)
  4. 4 Style → layout → paint → composite (only if a frame is due)
  5. 5 Loop back: pull the next task
Quiz

Inside a `setTimeout` handler you call `Promise.resolve().then(work)`. When does `work` run?

Quiz

A click handler runs for 400 ms. Which of these continues to work during the block?

Complete the analogy

In the barista metaphor, customer orders sit on the counter — that is the task queue. The barista's own sticky notes from the previous order, which she finishes before greeting the next customer, are which kind of queue?

Compute it

A task takes longer than this many milliseconds and the browser officially calls it a 'long task' that contributes to a poor INP score.

ms
Recall before you leave
  1. 01
    What are the three players in the browser event loop?
  2. 02
    Why does a 400 ms click handler freeze the page even though CSS animations are still running?
  3. 03
    What is the W3C threshold for a 'long task', and why does it matter for INP?
Recap

The browser event loop is a single-threaded engine that processes one task at a time — a setTimeout callback, a click handler, a parsed HTML chunk. After each task, it drains the microtask queue completely (promise resolutions, MutationObserver callbacks) before it can render or handle input. This makes the microtask queue behave as an extension of the running task from the renderer’s perspective. Tasks longer than 50 ms are long tasks; they delay both rendering and user-input handling, directly inflating the INP metric. The frame budget at 60 fps is 16.67 ms, so a 200 ms task drops roughly 12 frames and the user perceives obvious jank.

Connected lessons
appears again in143
Continue the climb ↑Tasks, microtasks, and scheduler.yield()
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.