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/messageevent. - Terminate:
worker.terminate(); inside workerself.close(). - No DOM: no
document/windowUI APIs; havefetch, timers, IndexedDB, WebAssembly, etc. - Transfer:
ArrayBuffer,MessagePort, some streams — zero-copy move. - Errors:
worker.onerror,unhandledrejectionin 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.
🔗 Related
- structured_clone.md — clone/transfer
- typed_arrays.md — binary payloads
- event_loop.md — main thread vs worker
- abort_controller.md — cancel work
- performance.md — measuring offload wins