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.

JavaScript's execution model runs application code, DOM updates, and rendering all on a single main thread, which means any sufficiently long-running synchronous computation freezes the entire page — no scrolling, no clicks, no rendering — until it finishes. Web Workers provide a way to run genuinely CPU-intensive JavaScript on a separate background thread, keeping the main thread free to remain responsive to user interaction while the heavy computation runs in parallel.

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
Creating a worker (new Worker('worker.js')) spins up a separate thread running an entirely different script file, with its own independent global scope, completely isolated from the main thread's variables, DOM access, and call stack — critically, a worker has *no* access to the DOM at all, which is by design, since the DOM is not thread-safe and allowing concurrent access from multiple threads would introduce serious correctness problems. Communication between the main thread and a worker happens exclusively through message passing: worker.postMessage(data) sends data to the worker, and the worker's self.onmessage handler (or addEventListener('message', ...)) receives it, doing the same in reverse for sending results back to the main thread.
new Worker('worker.js')worker.postMessage(data)self.onmessageaddEventListener('message', ...)
2
By default, data passed via postMessage is copied (using the same structured clone algorithm covered under deep copying) rather than shared directly — this avoids classic concurrent-access bugs (two threads mutating the same memory simultaneously) but means large data transfers involve a real copying cost. For genuinely large binary data (like a big ArrayBuffer), 'transferable objects' let you transfer ownership of the underlying memory directly to the worker instead of copying it, which is far faster for large payloads but means the original thread loses access to that data once transferred.
postMessageArrayBuffer
3
Web Workers are the right tool specifically for CPU-bound work — heavy computation, data processing, image manipulation — that would otherwise block the main thread; they are *not* useful for I/O-bound async work like network requests, since fetch and promises are already non-blocking on the main thread without needing a separate thread at all. A related, more specialized worker type, the Service Worker, serves an entirely different purpose (network request interception, offline caching) and is not interchangeable with a general-purpose Web Worker.
fetch