38. Web Workers: postMessage, structured clone, transferable objects
A dedicated Worker runs a separate script on its own OS thread, with its
own global scope (self, not window) — no DOM access, but full access to
fetch, timers, and (crucially) heavy computation that would otherwise
freeze the UI.
// main.js
const worker = new Worker("worker.js");
worker.postMessage({ cmd: "compute", n: 40 });
worker.onmessage = (e) => console.log("fibonacci result:", e.data.result);
worker.onerror = (e) => console.error("worker crashed:", e.message);
// worker.js
self.onmessage = (e) => {
if (e.data.cmd === "compute") {
const result = fib(e.data.n); // blocks the WORKER thread, not the main thread
self.postMessage({ result });
}
};
function fib(n) { return n <= 1 ? n : fib(n - 1) + fib(n - 2); }
Data passed via postMessage is copied, not shared, using the
structured clone algorithm — which handles objects, arrays, Map/Set,
Date, and typed arrays, but NOT functions, DOM nodes, or class instances
with methods/prototypes (only their plain-data shape survives).
worker.postMessage({ date: new Date(), items: new Map([["a", 1]]) }); // fine, structured-clonable
worker.postMessage({ fn: () => {} }); // throws DataCloneError — functions aren't clonable
Transferable objects avoid the copy entirely for large binary data — ownership of the underlying memory moves to the receiver, and the sender's reference becomes unusable (zero-copy, not "clone-then-delete"):
const buffer = new ArrayBuffer(64 * 1024 * 1024); // 64MB
worker.postMessage({ buffer }, [buffer]); // second arg: list of transferables
buffer.byteLength; // 0 — the sender no longer owns this memory at all