Web Workers
Running JavaScript in background threads — keeping the UI responsive during heavy computation.
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 functionMoving 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 playTreat 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: 1097Terminating a worker
// From the main thread — kill the worker immediately:
worker.terminate();
// From inside the worker itself:
// self.close();Official Sources
- MDN Web Docs — Web Workers API
- MDN Web Docs — Using Web Workers
- MDN Web Docs — SharedArrayBuffer
- Comlink — GitHub
The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.