How Chromium Handles JavaScript Promise Microtasks
This article provides an overview of how the Chromium web browser manages and executes JavaScript Promise microtasks at the conclusion of a standard task execution turn. By exploring the interaction between Chromium's Blink rendering engine and the V8 JavaScript engine, this guide explains the lifecycle of the microtask queue, the mechanics of the microtask checkpoint, and how the browser ensures all Promises are resolved before moving on to the next event loop task or rendering update.
The Architecture: Blink and V8
In Chromium, the event loop is orchestrated by Two main components: Blink (the rendering engine) and V8 (the JavaScript engine).
Blink manages the main thread's event loop, which handles standard
tasks (often called macrotasks) such as parsing HTML, handling user
input events, and executing setTimeout callbacks. V8, on
the other hand, is directly responsible for maintaining and executing
the JavaScript call stack and the Microtask Queue, which is where
Promise callbacks (.then(), .catch(),
.finally(), and code following an await)
reside.
Queuing the Microtasks
During a single task execution turn, synchronous JavaScript code runs
on the V8 call stack. When a Promise settles (either resolves or
rejects) during this execution, its associated reaction callbacks are
not executed immediately. Instead, V8 creates a
PromiseReactionJob and appends it to the end of its
internal Microtask Queue.
The End of a Task Turn
A task execution turn ends when the synchronous JavaScript code finishes running and the V8 JavaScript execution stack becomes completely empty. Before Chromium can return control to the Blink event loop to update the UI, paint the screen, or pick up the next macrotask, it must handle the pending microtasks.
Chromium manages this transition using a strict policy. Blink sets V8's microtask policy to track the depth of the JavaScript call stack. Every time a JavaScript function is entered, a scope counter increments; when the function exits, the counter decrements.
The Microtask Checkpoint
When the final JavaScript function on the stack exits and the execution depth counter reaches zero, Chromium triggers a Microtask Checkpoint. Here is the exact sequence of events that occurs during this checkpoint:
- Entering the Checkpoint: Blink signals V8 that the current task turn is concluding and invokes V8's microtask runner.
- Draining the Queue: V8 begins executing the microtasks in the Microtask Queue on a First-In, First-Out (FIFO) basis.
- Handling Chained Promises: If a running microtask
resolves another Promise or hits another
await, a new microtask is immediately appended to the back of the exact same Microtask Queue. - Exhaustion: V8 does not process a snapshot of the queue; it processes the live queue until it is completely empty. Any newly added microtasks are executed within the exact same microtask checkpoint.
Returning to the Event Loop
Only when the Microtask Queue is 100% depleted does V8 yield control
back to Blink. At this point, the task execution turn is officially
over. Chromium can now proceed with the rest of the event loop cycle,
which may include running requestAnimationFrame callbacks,
recalculating styles, updating the layout, painting the screen, or
pulling the next macrotask from the task queue.
Because V8 will not relinquish control until the microtask queue is empty, an infinite loop of resolving Promises will starve the Blink event loop, completely freezing the browser tab and preventing any UI rendering or user interaction.