You will use JavaScript Atomics to perform indivisible reads and writes on shared typed arrays, so multiple workers can update the same memory without corrupting values.
What it is
Atomics is a built-in JavaScript object that provides atomic operations on integer typed arrays backed by a SharedArrayBuffer. An atomic operation is treated as a single, indivisible step: other threads cannot observe a half-finished update, and two threads cannot interleave their modifications in a way that loses one update.
In browsers, SharedArrayBuffer may require a cross-origin isolated context.
The mental model is a small shared mailbox. A normal write is like scribbling in the mailbox while someone else is reading; an atomic operation is like handing over a sealed note. Related terms include SharedArrayBuffer, Int32Array, Worker, compare-and-swap, and lock-free coordination.
Why it matters
- Prevents lost updates when two workers increment the same counter.
- Prevents torn reads when a value is larger than a single machine word.
- Supports simple flags and sequence numbers for producer/consumer coordination.
- Provides building blocks for locks and queues using
Atomics.compareExchange,Atomics.wait, andAtomics.notify. - Makes shared-memory JavaScript safer without requiring a full mutex library.
Syntax or steps
- Create a
SharedArrayBuffer. - Wrap it in an integer typed array, usually
Int32Array. - Share the buffer with workers using
postMessage. - Use an Atomics method such as
Atomics.add(shared, 0, 1)instead ofshared[0] += value.
Example
Save the first snippet as main.js and the second as worker.js.
const sab = new SharedArrayBuffer(4);
const shared = new Int32Array(sab);
Atomics.store(shared, 0, 0);
const worker = new Worker('worker.js');
worker.onmessage = () => {
console.log(Atomics.load(shared, 0)); // 1
};
worker.postMessage(sab);
Worker file
self.onmessage = (event) => {
const shared = new Int32Array(event.data);
Atomics.add(shared, 0, 1);
self.postMessage('done');
};
Part by part: SharedArrayBuffer(4) reserves four bytes. Int32Array views those bytes as one 32-bit integer. Atomics.store initializes the shared slot. The main thread posts the buffer, and the worker creates its own view of the same memory. Atomics.add increments the value safely, and Atomics.load reads it without a race.
Common mistakes
- Using
shared[0] += 1on shared memory. This is read-modify-write and can lose updates. UseAtomics.add. - Calling
Atomics.waiton the main thread. It throws because blocking the UI thread is unsafe. Use a worker. - Assuming one atomic operation makes a whole algorithm atomic. Multi-step logic still needs compare-and-swap or a lock.
- Using a normal
ArrayBufferinstead ofSharedArrayBuffer. Atomics is intended for shared memory.
When to use it
| Approach | Best for | Limitation |
|---|---|---|
| Regular typed array access | Single-threaded code or non-shared memory | Races and torn reads in workers |
| Atomics | Simple shared counters, flags, and CAS loops | Does not automatically protect complex multi-step updates |
| Locks built with Atomics | Coordinating access to larger shared structures | More code and possible deadlock if misused |
Practice
Guided exercise: modify the example so two workers each add 1 to the shared counter. Expected output: 2.
Challenge: use Atomics.compareExchange(shared, 0, 0, 1) to implement a simple lock. Hint: the lock is acquired only when the previous value is 0.
Quick check
Question: What does Atomics.add(shared, 0, 5) return?
Answer: It returns the value stored at index 0 before adding 5.
Summary
Atomics gives JavaScript safe, indivisible operations on shared typed arrays. Use it when workers must update the same memory, and combine it with compare-and-swap or locks when you need to protect larger sequences of work.