Learn the concept
Event Loop & Runtime
Microtasks (Promises, queueMicrotask, MutationObserver) run immediately after the current script and before any macrotasks. Macrotasks (setTimeout, setInterval, I/O) run one per event loop iteration. The microtask queue is fully drained before the next macrotask executes.
The JavaScript event loop processes two types of asynchronous task queues:
Microtasks (higher priority):
Promise.then/catch/finally callbacksqueueMicrotask()MutationObserverprocess.nextTick() (Node.js — own queue, runs before Promise microtasks)Macrotasks (lower priority):
setTimeout / setIntervalsetImmediate() (Node.js)Rendering callbacks (separate from both queues):
requestAnimationFrame (runs during rendering phase)Execution order per event loop cycle:
Key insight: Microtasks can starve macrotasks — if microtasks keep adding more microtasks, setTimeout callbacks will be delayed indefinitely.
console.log('1: Synchronous');
setTimeout(() => {
console.log('2: Macrotask (setTimeout)');
}, 0);
Promise.resolve().then(() => {
console.log('3: Microtask (Promise)');
});
queueMicrotask(() => {
console.log('4: Microtask (queueMicrotask)');
});
console.log('5: Synchronous');
// Output order:
// 1: Synchronous
// 5: Synchronous
// 3: Microtask (Promise)
// 4: Microtask (queueMicrotask)
// 2: Macrotask (setTimeout)Using microtasks to batch multiple DOM updates before the browser renders, avoiding unnecessary repaints
Vue.js uses queueMicrotask to batch reactive state changes and apply DOM updates in a single tick
Scheduling a callback to run after current synchronous code but before any I/O or rendering via queueMicrotask
Build a visual simulator showing how microtask and macrotask queues are processed by the event loop
Build a task scheduler that prioritizes work using microtask and macrotask queues with configurable priorities