Yo folks, I’m wiring up a Web Worker to crunch a bunch of tasks and push results back to the UI, but I’m seeing stale results land after newer ones. I’m trying to keep the main thread snappy, but I also don’t want to leak memory by keeping every pending job forever.
const worker = new Worker("worker.js");
let nextId = 0;
const pending = new Map();
export function runJob(payload) {
const id = nextId++;
worker.postMessage({ id, payload });
return new Promise((resolve, reject) => {
pending.set(id, { resolve, reject });
});
}
worker.onmessage = (e) => {
const { id, result } = e.data;
const p = pending.get(id);
if (!p) return; // sometimes this hits
pending.delete(id);
p.resolve(result);
};
What’s the cleanest pattern to handle backpressure + cancellation so old jobs can’t “win” and I don’t end up with a growing pending Map?
A single worker gives you FIFO message handling, not FIFO completion. The moment worker.js yields (awaits anything, uses setTimeout, does chunking, etc.), later jobs can finish first and postMessage back first.
What’s worked cleanly for me is treating “promise resolution” and “UI is allowed to apply this” as two different concerns. Keep your pending map so every runJob() promise settles, but gate UI updates with a “latest request” marker per UI surface (search box, chart, whatever): store latestId = id when you kick off work, and when results arrive you still resolve the promise, but you only apply to UI when id === latestId. Old jobs don’t “win”, and you don’t need to keep old results around.
For backpressure + cancellation, I’d keep it boring: cap in-flight work and explicitly cancel. If pending.size is over some limit, either enqueue on the main thread or reject fast (your call), but don’t let it grow forever. For cancellation, send { type: "cancel", id } and have the worker check a cancelled set between chunks/before posting results; on the main thread, make sure you pending.delete(id) on success, error, and timeout/abort, otherwise you end up with a promise cemetery even if the UI ignores stale results.
1 Like