JavaScript Microtask Queue: Definition and Flush Timing
The JavaScript microtask queue is a high-priority queue within the
event loop designed to handle short, asynchronous tasks that must
execute immediately after the currently running script finishes. Unlike
standard macrotasks (such as setTimeout or I/O events),
microtasks guarantee execution before the browser performs any rendering
updates or proceeds to the next macrotask. This article explains what
the microtask queue is, the common APIs that use it, and the exact rules
governing when and how it is flushed.
What Is the Microtask Queue?
The microtask queue is an internal First-In, First-Out (FIFO) data structure managed by the JavaScript runtime environment (like V8 in Node.js and Chromium browsers). It holds callback functions that are deferred but need to run before control is returned to the event loop’s main cycle.
Common sources that enqueue microtasks include:
- Promises: Callbacks attached via
.then(),.catch(), and.finally(). queueMicrotask(): The explicit global API designed to queue a microtask directly.MutationObserver: Callbacks triggered by changes to the DOM tree in browser environments.process.nextTick()(Node.js): While technically placed in its own higher-priority queue in Node.js, it behaves similarly by executing ahead of the standard event loop phases.
When Does the Microtask Queue Flush?
The microtask queue flushes whenever the JavaScript call stack becomes completely empty (i.e., when the currently running synchronous execution context finishes).
Specifically, the flush occurs:
- After the completion of a synchronous script block: As soon as the global or top-level execution context finishes, all pending microtasks are immediately processed.
- After every macrotask callback: When a standard
task (such as a DOM event listener,
setTimeout, orsetIntervalcallback) finishes and its execution frame is popped off the call stack, the microtask queue is flushed immediately. - Before UI rendering: In web browsers, the microtask queue is completely drained before the browser recalculates styles, executes layout, and repaints the screen.
- Before picking the next macrotask: The event loop will never move to the next item in the macrotask queue until the microtask queue has reached a count of zero.
How the Flushing Process Works
When the runtime begins draining the microtask queue, it processes tasks continuously until the queue is completely empty.
If a running microtask enqueues another microtask, that newly scheduled microtask is appended to the current queue and will execute within the same flush cycle. Consequently, an infinite or recursive chain of microtasks will starve the event loop, preventing the UI from updating and blocking all subsequent macrotasks from executing.
Event Loop Execution Order
To understand the flush timing in context, the event loop processes tasks in the following strict order:
- Execute synchronous code on the call stack until it is empty.
- Flush the microtask queue: Run all queued microtasks sequentially until none remain.
- Perform rendering and painting steps (if required by the browser).
- Pick the next macrotask from the task queue.
- Repeat from Step 1.