Performanceadvanced
Web Workers for Offloading Heavy Computation
Running JavaScript on a separate background thread to keep the main thread responsive during CPU-intensive work.
A worker runs JavaScript on another thread. The main thread stays responsive.
A Web Worker is like hiring a specialist you send instructions to via sealed envelopes (postMessage) rather than letting them wander freely through your own office (the DOM) — they do their heavy, time-consuming work entirely off to the side in their own separate workspace, and mail back a sealed envelope with the finished result once done, all while you keep helping customers (staying responsive to the user) at your own desk the entire time.
Key Concepts
1
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ data: bigArray });
worker.onmessage = (e) => render(e.data);2
// worker.js
self.onmessage = (e) => {
const result = heavyComputation(e.data.data);
self.postMessage(result);
};3
Why it is needed. A long synchronous computation on the main thread blocks everything — no rendering, no
clicks, no scrolling. The page is frozen, not slow.
4
for (let i = 0; i < 1e9; i++) {} // the tab stops responding5
Splitting the work with setTimeout helps and is still on the same thread. A worker genuinely runs
elsewhere.
setTimeout
6
What a worker cannot touch.
no DOM
no window, no document
no direct access to the page's variables7
It has fetch, WebSocket, IndexedDB, timers and most of the language. Anything involving the page must
be sent back and done on the main thread.
fetchWebSocketIndexedDB
8
Messages are copied, not shared. The structured clone algorithm serialises what you send, so a large
object costs time to transfer:
9
worker.postMessage(hugeArray); // copied -- can be slow10
Transferables move ownership instead of copying.
worker.postMessage(buffer, [buffer]); // instant; the sender
// loses access11
For an ArrayBuffer that is the difference between a copy of ten megabytes and a pointer.
ArrayBuffer
12
And SharedArrayBuffer shares memory outright, with Atomics for coordination — powerful, requires
specific security headers, and needs care.
SharedArrayBufferAtomics
13
Where workers are worth it. Image and video processing, large parsing, cryptography, physics, search
indexing over a big dataset, and anything measured in hundreds of milliseconds.
14
Where they are not. Short tasks — the messaging overhead exceeds the work. Anything DOM-heavy. And
anything where the result is needed synchronously.
15
The practical note. Workers are a separate file, or a blob URL, which complicates bundling. Vite and
Webpack both handle new Worker(new URL('./w.js', import.meta.url)), and getting that syntax right is most
of the setup friction people hit.
16
What the interviewer is probing.1. "What can a worker not do?" Probing: the limits. Stalls: "Nothing." Moves up: it has no
DOM access and no window — it communicates by messages only.
17
2. "How is data passed, and what does it cost?" Probing: the copy. Stalls: "By reference."
Moves up: structured clone copies it, which is expensive for large payloads; transferable objects
move an ArrayBuffer with no copy.
18
3. "When is a worker worth it?" Probing: judgement. Stalls: "For all async work." *Moves
up:* genuinely CPU-bound work — parsing, image processing, crypto; it does nothing for I/O, which is
already asynchronous.
19
4. "What is the latency cost?" Probing: the trade. Stalls: "None." Moves up: startup and
message passing, so very small tasks are slower in a worker than on the main thread.