Articles

What Is a Web Worker? Running JS Off the Main Thread

A web worker runs JavaScript on a background thread, freeing the main thread to keep the UI responsive. How workers communicate and when to use one.

Takina Takina · · 4 min read
A laptop screen showing code in a dark editor

A web worker is a JavaScript program that runs on a separate background thread, independent of the main thread that renders the page and responds to clicks. Because JavaScript in the browser is normally single-threaded, any expensive computation — parsing a huge JSON payload, resizing an image, running a search index — blocks that one thread and freezes the UI until it finishes. A worker moves that work off the main thread so scrolling, typing, and animations stay smooth while it runs.

Why JavaScript needs this

The browser’s main thread does double duty: it executes your application’s JavaScript and it handles layout, painting, and input events. When a synchronous function takes 400 milliseconds to run, the browser can’t process a click or repaint a frame during that window — the page appears to hang. This is a different problem from the one solved by async/await and promises, which manage when code runs relative to I/O but still execute on the same single thread. A promise resolving doesn’t give you a second CPU core; a worker does.

Understanding this distinction requires knowing how the JavaScript event loop works: callbacks, promise resolutions, and rendering all compete for the same thread’s attention. A long synchronous task starves everything else queued behind it, no matter how it was scheduled. Workers sidestep the event loop entirely by giving that task its own thread with its own event loop.

How workers communicate

A worker doesn’t share memory with the page that spawned it. Instead, the two sides exchange data by message passing:

// main.js
const worker = new Worker("worker.js");
worker.postMessage({ command: "process", data: largeArray });
worker.onmessage = (event) => {
  console.log("Result:", event.data);
};

// worker.js
self.onmessage = (event) => {
  const result = expensiveComputation(event.data.data);
  self.postMessage(result);
};

postMessage() serializes the data being sent (using the structured clone algorithm, which handles objects, arrays, and typed arrays but not functions or DOM nodes) and copies it to the other side. Nothing is shared by reference by default, which is what makes this safe — there’s no risk of both threads mutating the same object at the same time. For very large datasets where copying is itself expensive, Transferable objects like ArrayBuffer can be moved between threads instead of copied, at the cost of the sender losing access to it.

What a worker can and can’t do

Workers run in a restricted global scope, not the full window object:

  • No DOM access. A worker can’t touch document, manipulate elements, or read layout. Any UI update has to happen back on the main thread after the worker posts its result.
  • Full access to computation APIs. fetch, setTimeout, typed arrays, WebAssembly, and most JavaScript language features work normally inside a worker.
  • Its own error boundary. An uncaught exception in a worker doesn’t crash the page; it fires an error event that the main thread can listen for.

This makes workers a good fit for pure computation — parsing, compression, cryptography, sorting large datasets — and a poor fit for anything that needs to read or write the page directly.

Types of workers

TypeScopeTypical use
Dedicated workerOne-to-one with the page that created itOffloading a single expensive task
Shared workerShared across multiple tabs/windows of the same originCoordinating state between open tabs
Service workerRuns independently of any page, even when it’s closedOffline caching, push notifications

A service worker is often confused with a plain web worker because the names are similar, but they solve different problems. A service worker acts as a network proxy for caching and offline support and can outlive the page entirely; a dedicated web worker exists to run computation in parallel with a page that’s currently open and disappears when that page closes or terminates it.

When to reach for one

Workers earn their complexity when a task is both CPU-bound and large enough to visibly affect frame rate — generally anything that would otherwise block the main thread for more than a few tens of milliseconds. Common cases:

  • Parsing or transforming large JSON or CSV files client-side.
  • Client-side image or video processing (resizing, filters, format conversion).
  • Running search or filtering over a large in-memory dataset.
  • Cryptographic operations like hashing or encryption on large payloads.
  • Complex calculations behind data visualizations that update on user input.

For smaller tasks, the overhead of spinning up a worker and serializing messages back and forth can outweigh the benefit — debouncing or throttling the work on the main thread is often simpler and sufficient. Workers also pair naturally with WebAssembly: compute-heavy Wasm modules are frequently run inside a worker so their execution never touches the main thread at all.

The takeaway

A web worker gives JavaScript a genuine background thread, so expensive computation doesn’t freeze scrolling, typing, or animation. It trades direct DOM access and shared memory for isolation and safety, communicating with the main thread through copied messages instead. Reach for one when a task is CPU-bound and long enough to visibly stall the page — parsing, image processing, cryptography, large in-memory computation — and keep DOM updates on the main thread where they belong.

Takina Takina · · 4 min read

requestIdleCallback Explained

requestIdleCallback runs low-priority JavaScript when the browser is idle, without blocking rendering, input, or the main thread.

#JavaScript #Performance #Web Development
Takina Takina · · 5 min read

Finding and Fixing Memory Leaks in JavaScript

A JavaScript memory leak happens when a reference outlives its usefulness and the garbage collector can't reclaim it. Common causes and how to find them.

#JavaScript #Web Development #Performance