One cycle: where your code leaves marks, and when the loop reads them
Task queue FIFO · the loop takes the oldest off the front
now running click handler
1setTimeout callback
2network event
2 waiting
1 Run one task to completion
your code
p.textContent = i marks the node dirty
.then(fn) await queueMicrotask(fn) queued
requestAnimationFrame(fn) added to the callback list
runs to completion — no pixels while it runs
2 Microtask checkpoint FIFO
fn fn
queued — can't run while the task does the open checkpoint runs it
queued during the drain → same drain. Drains to empty.
3 Maybe render
frame due?
a rendering opportunity · ~8.3ms at 120Hz, 16.7ms at 60Hz
no skip it
nothing on screen changed
↺ back to the task queue, marks untouched
yes render step
frame callbacks style layout paint composite
dirty set cleared · frame on screen · ↺ back to the task queue
Waiting on a frame not a queue
Held by the document read only by the render step
Dirty nodes <p> · needs style, layout a flag on the node, set the instant the write returns nothing yet
Frame callbacks fn from requestAnimationFrame nothing yet
a set, not a queue — nothing here runs until the loop says a frame is due
cleared
Your code never renders. It leaves marks in two places — a queue that drains every cycle, and a set the document holds onto until the loop decides a frame is due.
The render step is the only reader of the dirty set, and it only runs when a frame is due.