← Lessons

quiz vs the machine

Gold1420

Frontend

Microtask Timing

Why promise callbacks beat setTimeout and can block paint.

5 min read · core · beat Gold to climb

Two queues

The event loop has a task queue for things like timers and events, and a microtask queue for promise reactions and queueMicrotask callbacks.

The key rule

After every task, and after each callback that empties the JavaScript stack, the browser drains all microtasks before doing anything else. Microtasks therefore run before the next task and before the next render.

Ordering example

  • A resolved promise then handler is a microtask and runs before a setTimeout of zero, which is a task.
  • New microtasks queued while draining are also run in the same drain, before yielding.

The starvation risk

Because the drain continues until the queue is empty, a microtask that keeps scheduling more microtasks can loop forever and never let the browser render. The page locks up even though no single callback is long.

Practical guidance

  • Use microtasks for small follow up work that should happen before paint.
  • Use a task, such as setTimeout or a message channel, when you deliberately want to yield to rendering.

Key idea

Microtasks drain fully after each task and before rendering, so they run before timers but can starve paint if they keep enqueuing themselves.

Check yourself

Answer to earn rating on the learn ladder.

1. Which runs first, a resolved promise callback or a setTimeout of zero?

2. How can microtasks starve rendering?