Fundamentals 10 min read

Understanding Google V8 Engine Promise Implementation and the JavaScript Event Loop

This article explains the evolution of Google V8's Promise implementation, details the JavaScript single‑threaded event loop with macro‑ and micro‑tasks, describes various programming models, and walks through the internal V8 code that realizes Promise construction, chaining, and utility methods such as Promise.all and Promise.race.

Xueersi Online School Tech Team
Xueersi Online School Tech Team
Xueersi Online School Tech Team
Understanding Google V8 Engine Promise Implementation and the JavaScript Event Loop

Event Loop

JavaScript runs on a single thread that contains a unique event loop. While the event loop is singular, a thread can have multiple task queues, which are divided into macro‑tasks (e.g., setTimeout, setInterval) and micro‑tasks (e.g., process.nextTick, Promise, Object.observe).

Different tasks are placed into different queues; Promise belongs to the micro‑task queue and is processed there.

Programming Model

There are three common programming models: single‑threaded synchronous, multi‑threaded synchronous, and asynchronous programming.

The single‑threaded synchronous model works like a FIFO queue where each task must wait for the previous one to finish. The multi‑threaded synchronous model overcomes this waiting but incurs high context‑switch costs. JavaScript’s authors realized that most latency comes from I/O waiting, which does not require expensive CPU resources.

In JavaScript, the runtime places synchronous code on the call stack; when the stack is empty, the main thread first executes all micro‑tasks, then macro‑tasks, repeating this cycle until both stacks are cleared.

What Problem Does Promise Solve?

JavaScript treats functions as first‑class citizens, and asynchronous operations are wrapped as callbacks placed in task queues. While this improves performance, deeply nested callbacks become hard to maintain. Promise was created to address this callback‑hell issue.

Google V8 Promise.js Implementation

Overall Implementation

Promise.js defines a $Promise constructor and uses PromiseSet to initialize each Promise object, which has four main properties: value, status (0 = pending, +1 = resolved, -1 = rejected), onResolve (queue of resolve callbacks), and onReject (queue of reject callbacks). The raw variable represents an empty JavaScript object, and InternalArray corresponds to the native Array.

The global variable represents the browser’s window object, and macros such as %AddNamedProperty attach $Promise to it.

Functions like defer, accept, reject, all, race, resolve are installed on $Promise, while chain, then, catch are placed on $Promise’s prototype.

Constructor

The constructor receives a resolver function and passes two functions to it: function(x) { PromiseResolve(promise, x) } and function(r) { PromiseReject(promise, r) } , which serve as the resolve and reject callbacks used inside a Promise.

PromiseResolve and PromiseReject

These functions are thin wrappers around PromiseDone, which handles the actual state transition.

PromiseDone

PromiseDone checks whether the Promise’s state has already changed; if not, it enqueues the task via PromiseEnqueue and then updates the state and value.

PromiseEnqueue

PromiseEnqueue uses the %EnqueueMicrotask macro to wrap a task in PromiseHandle and push it onto the micro‑task queue, ensuring it runs after the current call stack clears.

PromiseThen

PromiseThen enables chaining by delegating to PromiseChain, which registers onResolve and onReject callbacks along with a deferred object that holds the next Promise in the chain.

PromiseCoerce

PromiseCoerce simply returns the value passed from a resolved Promise.

PromiseChain

Each then call creates a deferred object (containing a new pending Promise and its resolve/reject functions). Depending on the current Promise’s state, callbacks are either stored for later execution or immediately enqueued as micro‑tasks.

PromiseAll

Promise.all creates a deferred object, tracks the number of unresolved Promises, and resolves when all have succeeded or rejects as soon as any Promise rejects, enabling concurrent execution of independent asynchronous operations.

PromiseOne

PromiseOne implements a timeout‑like behavior: if any of several Promises does not settle within a specified period (e.g., 2 seconds), it rejects, allowing callers to avoid indefinite waiting on slow external services.

JavaScriptV8asyncEvent LoopPromiseMicrotasks
Xueersi Online School Tech Team
Written by

Xueersi Online School Tech Team

The Xueersi Online School Tech Team, dedicated to innovating and promoting internet education technology.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.