15. Timers and scheduling: setTimeout, rAF, requestIdleCallback, MessageChannel
Not all "run this later" APIs are equal — each is a different macrotask source tuned for a different purpose:
| API | Fires | Use for |
|---|---|---|
setTimeout(fn, 0) |
Next macrotask turn, but browsers clamp nested timeouts to a minimum ~4ms after 5 levels of nesting | General deferred work |
requestAnimationFrame(fn) |
Right before the next repaint, synced to the display's refresh rate (~60Hz = 16.6ms) | Any visual animation — never animate with setInterval |
requestIdleCallback(fn) |
During browser idle periods, with a deadline you must respect | Low-priority background work (analytics batching, prefetch) that shouldn't compete with rendering |
queueMicrotask(fn) |
End of the current task, before the next macrotask/paint (§12) | Deferring work to just after the current synchronous block, without a full event-loop turn |
MessageChannel |
Macrotask, but with no minimum delay clamp unlike setTimeout(fn, 0) |
The fastest legal way to yield to the event loop — used internally by React's scheduler |
function animate() {
element.style.transform = `translateX(${x++}px)`;
if (x < 300) requestAnimationFrame(animate); // self-scheduling, paced to the display
}
requestAnimationFrame(animate);