Skip to content
Painter
Deep dives

The event loop, one frame at a time

Sixteen milliseconds, and everything that has to fit inside them before the screen updates.

At 60 frames per second the browser has about 16.7 ms to run your tasks, process input, run animation callbacks, recalculate style, lay out, paint and composite. Miss the budget and the frame is simply not drawn.

The ordering is fixed and worth memorising, because it explains almost every animation bug you will ever file.

  • Tasks first, one at a time, in the order they were queued.
  • Then microtasks, drained completely, which is why a promise loop can starve rendering entirely.
  • Then requestAnimationFrame callbacks, then style, layout, paint, composite.

Microtasks do not yield

An unbounded chain of promises will never let a frame render. If a loop must run, break it with setTimeout, not with another then.

Comments

3 Oldest first
Greta Halloway

The microtask starvation point explains a bug I filed against a framework last year and never got to the bottom of. It was our own promise loop.

Kofi Mensah

Would love a follow-up on where scheduler.yield fits into this now that it is shipping.

chrisAuthor

It is on the list. Short version: it does what the setTimeout trick does, without the four-millisecond floor.

Leave a comment

Real questions get real answers. The editorial team reads every thread.