Microtask Queue
The microtask queue holds callbacks scheduled by Promise.then/catch/finally and queueMicrotask(). It is processed after every task completes, before the event loop picks up the next macrotask.
Microtasks are VIP guests who cut the line — after each regular guest (macrotask) is served, ALL VIPs jump in and get served before the next regular guest, even if more VIPs keep showing up.
After each macrotask (or after initial script execution), the event loop drains the entire microtask queue — not just one item, but all of them, including any microtasks added during draining. This means a recursive Promise chain can starve the macrotask queue indefinitely. MutationObserver callbacks are also microtasks. The order is: current task → drain all microtasks → render step (browser) → next macrotask.
The spec defines microtask checkpoints at multiple points: after each task, after each microtask (nested), and at various Web API integration points. In Node.js, process.nextTick() callbacks run in a separate 'nextTick queue' that is actually drained BEFORE the Promise microtask queue within each iteration of libuv — a Node-specific behavior not in the browser. This means process.nextTick is effectively 'higher priority' than Promise.then in Node. In browsers, the microtask checkpoint is called after every callback execution, including event handler callbacks inside a task.
The microtask queue is drained completely after every task — all Promise .then callbacks run before any setTimeout callback, even with 0ms. This matters for ordering guarantees: anything you need to run 'immediately after this synchronous block' should use Promise.resolve().then() or queueMicrotask(), not setTimeout(fn, 0).
In Node.js, process.nextTick callbacks run before Promise microtasks — this is a Node-specific deviation from the browser's event loop model. Code relying on nextTick vs Promise ordering will behave differently in browser environments.