All topics
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 responding
5
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 variables
7
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 slow
10
Transferables move ownership instead of copying.
    worker.postMessage(buffer, [buffer]);   // instant; the sender
                                            // loses access
11
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.