Atomics.wait and Atomics.notify in Web Workers

Atomics.wait and Atomics.notify provide a low-level synchronization mechanism for managing concurrent JavaScript execution across Web Workers. When workers share memory via a SharedArrayBuffer, these methods allow threads to pause execution and wait for specific state changes without consuming CPU resources through busy-waiting, and then signal each other when data is ready to be processed.

The Need for Synchronization

When multiple Web Workers operate on the same SharedArrayBuffer, they risk data races if one worker reads from a memory location while another writes to it. While basic Atomics operations (such as Atomics.add or Atomics.exchange) ensure individual read-modify-write operations are indivisible, complex tasks require threads to pause and wait for multi-step processes to complete.

Without synchronization primitives, workers would need to poll memory inside a loop (while loop), which maxes out CPU cores and reduces performance. Atomics.wait and Atomics.notify eliminate polling by leveraging the operating system’s native thread-blocking capabilities.

How Atomics.wait Works

Atomics.wait() blocks a worker thread until a specific condition is met or an optional timeout expires.

Constraint: To prevent user interface freezes, Atomics.wait() is forbidden on the main thread and will throw a TypeError if invoked there. It can only be called inside dedicated Web Workers.

How Atomics.notify Works

Atomics.notify() wakes up one or more workers that are currently suspended in an Atomics.wait() call on a specified array index.

Unlike Atomics.wait(), Atomics.notify() can be executed safely from both Web Workers and the main thread.

The Coordination Workflow

A standard producer-consumer pattern between workers follows this sequence:

  1. Setup: Both the producer worker and consumer worker receive the same SharedArrayBuffer wrapped in an Int32Array.
  2. Consumer Waits: The consumer checks a synchronization index (e.g., index 0, initialized to 0). It calls Atomics.wait(sharedArray, 0, 0). The consumer enters a sleep state.
  3. Producer Works: The producer writes data to the buffer, updates index 0 to a new state (e.g., 1), and calls Atomics.notify(sharedArray, 0, 1).
  4. Consumer Resumes: The consumer thread wakes up, receives "ok", reads the newly produced data, and resets the synchronization index.

Key Rules and Restrictions