Skip to content

Worker Patterns

Workers are asynchronous and may handle many tasks. To match a response to the right request, attach a unique id to each message:

main.js
let nextId = 0;
const pending = {};
worker.onmessage = (e) => {
const { id, result } = e.data;
if (pending[id]) { pending[id](result); delete pending[id]; }
};
function ask(payload) {
return new Promise((resolve) => {
const id = ++nextId;
pending[id] = resolve;
worker.postMessage({ id, payload });
});
}
// worker.js
self.onmessage = (e) => {
const { id, payload } = e.data;
self.postMessage({ id, result: process(payload) });
};

Wrapping the pattern above into a small helper makes calling the worker feel like a regular async function call.

function createRpc(worker) {
let seq = 0;
const map = {};
worker.onmessage = (e) => {
const cb = map[e.data.id];
if (cb) { cb(e.data.result); delete map[e.data.id]; }
};
return {
call(method, args) {
return new Promise((res) => {
const id = ++seq;
map[id] = res;
worker.postMessage({ id, method, args });
});
}
};
}

Uncaught errors inside a worker fire worker.onerror in the main thread — not window.onerror. Always attach a handler:

worker.onerror = (event) => {
console.error('Worker error:', event.message, 'at', event.filename, ':', event.lineno);
event.preventDefault(); // suppress browser console error
};

You can also attach self.onerror inside the worker itself to catch async errors before they bubble up to the main thread.

Use a workerDo NOT use a worker
CPU-intensive loops (crypto, image processing, compression)Simple DOM reads/writes
Parsing large JSON or binary dataShort tasks < 5 ms
Real-time data processing (audio, video)Tasks needing direct DOM access
Keeping the UI at 60 fps during heavy workOne-off simple async fetches (use fetch directly)

Runnable: request/response with message IDs

Section titled “Runnable: request/response with message IDs”
Browser Storage
Why include a unique `id` in each worker message?
Where does an uncaught error inside a Web Worker fire?
Which task is a GOOD use case for a Web Worker?
In the tiny RPC pattern, what does the `map` object store?