Code Reference

Web Workers

JavaScript · Reference cheat sheet

Web Workers

JavaScript · Reference cheat sheet


📋 Overview

Web Workers run scripts off the main thread so heavy CPU work does not block UI. Dedicated workers are owned by one page; shared workers can be used by multiple; service workers intercept network (separate topic). Communicate via postMessage and structured clone / transferables.

🔧 Core concepts

  • Create: new Worker(new URL("./worker.js", import.meta.url), \{ type: "module" \}).
  • Messages: worker.postMessage(data, transfer?) / onmessage / message event.
  • Terminate: worker.terminate(); inside worker self.close().
  • No DOM: no document/window UI APIs; have fetch, timers, IndexedDB, WebAssembly, etc.
  • Transfer: ArrayBuffer, MessagePort, some streams — zero-copy move.
  • Errors: worker.onerror, unhandledrejection in module workers.
const worker = new Worker(new URL("./heavy.js", import.meta.url), {
  type: "module",
});
worker.postMessage({ op: "sum", values: [1, 2, 3] });
worker.onmessage = (e) => console.log(e.data);

💡 Examples

// main.js
const w = new Worker(new URL("./worker.js", import.meta.url), { type: "module" });
w.postMessage({ n: 40 });
w.addEventListener("message", (e) => {
  console.log("fib", e.data);
  w.terminate();
});

// worker.js
self.onmessage = (e) => {
  const { n } = e.data;
  self.postMessage(fib(n));
};
function fib(n) {
  return n < 2 ? n : fib(n - 1) + fib(n - 2);
}

// Transfer large buffer
const bytes = new Uint8Array(1_000_000);
worker.postMessage(bytes, [bytes.buffer]); // bytes.buffer detached

// MessageChannel for ports
const { port1, port2 } = new MessageChannel();
worker.postMessage({ port: port2 }, [port2]);
port1.onmessage = (e) => console.log(e.data);
// Pool sketch
class Pool {
  constructor(size, url) {
    this.idle = Array.from(
      { length: size },
      () => new Worker(url, { type: "module" }),
    );
  }
  run(data) {
    const w = this.idle.pop();
    return new Promise((resolve, reject) => {
      w.onmessage = (e) => {
        this.idle.push(w);
        resolve(e.data);
      };
      w.onerror = reject;
      w.postMessage(data);
    });
  }
}

⚠️ Pitfalls

  • Classic workers need separate classic scripts; prefer \{ type: "module" \} + import.meta.url.
  • Cloning large object graphs is expensive — transfer buffers or use SharedArrayBuffer (COOP/COEP).
  • Workers don’t share memory with the page (except SAB) — no shared closures.
  • Forgetting terminate() leaks threads.
  • DOM / most Web APIs involving UI are unavailable — design data in/out carefully.

On this page