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.

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:

  1. Entering the Checkpoint: Blink signals V8 that the current task turn is concluding and invokes V8's microtask runner.
  2. Draining the Queue: V8 begins executing the microtasks in the Microtask Queue on a First-In, First-Out (FIFO) basis.
  3. 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.
  4. 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.