A task (macrotask) is a unit of work the event loop starts: running a <script>, dispatching an event, or firing a setTimeout() or setInterval() callback. A microtask is a short job queued by promise reactions, await continuations, MutationObserver or queueMicrotask(). After each task the engine drains the entire microtask queue, including microtasks queued meanwhile, before the next task or a repaint.
console.log("A sync");
setTimeout(() => {
console.log("B timeout 1");
Promise.resolve().then(() => console.log("C microtask queued by timeout 1"));
}, 0);
setTimeout(() => console.log("D timeout 2"), 0);
Promise.resolve().then(() => {
console.log("E then");
queueMicrotask(() => console.log("F microtask queued by a microtask"));
});
console.log("G sync");A sync G sync E then F microtask queued by a microtask B timeout 1 C microtask queued by timeout 1 D timeout 2
C beats D because microtasks drain between tasks, and browsers print the same order. A microtask that keeps queuing microtasks therefore freezes the page. Node.js 2,131 drains its process.nextTick() queue just before the microtask queue: a nextTick callback runs before E in a CommonJS file but after F in an ES module, which already executes inside a microtask.