← thecodex.expert · The Codex Family of Knowledge
The Codex / JavaScript / Web Workers

Web Workers

Running JavaScript in background threads — keeping the UI responsive during heavy computation.

What it is, in plain English: A Web Worker runs JavaScript in a background thread, separate from the main thread that handles the UI. This means heavy computation can run without freezing the page. Workers communicate with the main thread via postMessage() and onmessage — they can't access the DOM directly. SharedArrayBuffer enables shared memory between workers, with Atomics for thread-safe operations.
Where this bites you

postMessage sends a copy of your data (a structured clone), and the copy keeps only the data, not the class. Post an instance of your own class to a worker and what arrives is a plain object with the same fields but none of the methods. The first order.total() inside the worker throws, and from the page all you see is a vague worker error event.

// main.js
class Order {
  constructor(items) { this.items = items; }
  total() { return this.items.reduce((sum, i) => sum + i.price * i.qty, 0); }
}

const worker = new Worker("order-worker.js");
worker.onmessage = (e) => console.log("total:", e.data);
worker.onerror   = (e) => console.log("worker error:", e.message);
worker.postMessage(new Order([{ price: 499, qty: 2 }, { price: 99, qty: 1 }]));

// order-worker.js
self.onmessage = (event) => {
  const order = event.data;          // arrives as a plain object: the class is gone
  self.postMessage(order.total());
};
// worker error: Uncaught TypeError: order.total is not a function

Moving heavy work off the main thread

JavaScript runs on a single main thread — the same thread that handles user clicks, animations, and DOM updates. If your code does something heavy (processing a large dataset, running image filters, parsing a huge file), it blocks everything else. The page freezes. Buttons don't respond.

Web Workers solve this by running code in a separate background thread.

Creating a worker

// worker.js — runs in background thread
// No access to: window, document, DOM
// Full access to: fetch, setTimeout, IndexedDB, crypto, WebSockets, Canvas (OffscreenCanvas)

self.onmessage = function(event) {
  const numbers = event.data;  // received from main thread

  // Heavy computation — doesn't block the UI:
  const sorted = [...numbers].sort((a, b) => a - b);
  const sum    = sorted.reduce((s, n) => s + n, 0);
  const mean   = sum / sorted.length;

  // Send result back:
  self.postMessage({ sorted, mean });
};
// main.js — the page's JavaScript
const worker = new Worker("worker.js");

// Send data to the worker:
const bigArray = Array.from({ length: 1000000 }, () => Math.random());
worker.postMessage(bigArray);

// Receive result from worker:
worker.onmessage = (event) => {
  const { sorted, mean } = event.data;
  console.log("Mean:", mean.toFixed(4));
  displayResults(sorted);
};

worker.onerror = (error) => {
  console.error("Worker failed:", error.message);
};

// The page stays responsive while the worker computes!
// Buttons still work, animations still play

Treat messages as plain data, as the number-array example above does. When you need the behaviour on the other side, put the class in a shared module that both the page and a module worker import (module workers are covered in the Intermediate tab), and rebuild the object from the data it receives:

// order.js -- shared by the page and the worker
export class Order {
  constructor(items) { this.items = items; }
  total() { return this.items.reduce((sum, i) => sum + i.price * i.qty, 0); }
  static from(data) { return new Order(data.items); }   // rebuild from plain data
}

// order-worker.js (module worker)
import { Order } from "./order.js";

self.onmessage = (event) => {
  const order = Order.from(event.data);   // turn the plain copy back into an Order
  self.postMessage(order.total());
};

// main.js (<script type="module">)
const worker = new Worker("order-worker.js", { type: "module" });
// total: 1097

Terminating a worker

// From the main thread — kill the worker immediately:
worker.terminate();

// From inside the worker itself:
// self.close();
Try It Yourself

Official Sources

The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.