setTimeout(fn, 0) runs fn later as a new task, after rendering may happen. queueMicrotask(fn) runs fn right after the current code, before any other task or rendering.
setTimeout(fn, 0):
- It schedules a new macrotask (task).
- It runs after the current task finishes and after all microtasks are done.
- In browsers, the page usually gets a chance to render first.
- The delay is a minimum, not a promise. When timers are nested inside each other more than about 5 levels deep, browsers clamp (raise) the delay to at least about 4 ms.
queueMicrotask(fn):
- It runs
fnas soon as the current call stack is empty. - It runs before any other task and before rendering.
- It is useful to run something right after the current synchronous work, in order. An example is batching several state updates into one.
The danger: microtasks added by microtasks keep the loop busy. A recursive chain of microtasks never gives control back. The page cannot render or handle input.
To split heavy work so the UI stays responsive, give control back with a macrotask, not a microtask. Options are setTimeout, MessageChannel, or scheduler.postTask where the browser supports it.
- A timer callback is a macrotask. It runs after all microtasks and possibly after a render.
- A microtask runs right after the current call stack, before rendering.
- The
setTimeoutdelay is a minimum. After about 5 levels of nesting, timers are clamped to about 4 ms. - Recursive microtasks can block rendering and input forever.
- For long work, give control back to the browser with macrotasks.
Read more