Performancemedium
Offload heavy parsing to a Web Worker (with cancellation)
You parse large JSON / log blobs in the browser and the main thread janks. Wrap a Web Worker in a clean Promise-based API with cancellation and request IDs.
Asked at CrowdStrike, Google
Answer
// worker.js — runs off the main thread self.onmessage = (e) => { const { id, payload } = e.data try { // Replace with real heavy work (parse, decompress, indexing, etc.) const result = JSON.parse(payload) self.postMessage({ id, ok: true, result }) } catch (err) { self.postMessage({ id, ok: false, error: String(err) }) } } // workerClient.js — main thread API export function createWorkerClient(workerUrl) { const worker = new Worker(workerUrl, { type: 'module' }) const pending = new Map() // id -> { resolve, reject, signal, onAbort } let nextId = 0 worker.onmessage = (e) => { const { id, ok, result, error } = e.data const entry = pending.get(id) if (!entry) return // already cancelled pending.delete(id) entry.signal?.removeEventListener('abort', entry.onAbort) ok ? entry.resolve(result) : entry.reject(new Error(error)) } function run(payload, { signal } = {}) { if (signal?.aborted) return Promise.reject(new DOMException('Aborted', 'AbortError')) return new Promise((resolve, reject) => { const id = nextId++ const onAbort = () => { if (!pending.has(id)) return pending.delete(id) reject(new DOMException('Aborted', 'AbortError')) // Cooperative cancel: tell the worker to drop this id if it supports it. worker.postMessage({ id, cancel: true }) } pending.set(id, { resolve, reject, signal, onAbort }) signal?.addEventListener('abort', onAbort, { once: true }) worker.postMessage({ id, payload }) }) } function terminate() { worker.terminate() pending.forEach((p) => p.reject(new Error('worker terminated'))) pending.clear() } return { run, terminate } } // Usage const client = createWorkerClient(new URL('./worker.js', import.meta.url)) const ctrl = new AbortController() client.run(bigJsonString, { signal: ctrl.signal }) .then((parsed) => render(parsed)) .catch((err) => { if (err.name !== 'AbortError') reportError(err) }) // Cancel if the user navigates away: ctrl.abort() // Interview talking points: // - Use Transferable objects (ArrayBuffer) for zero-copy where possible. // - Pool workers when calls are frequent (creation cost is ~1-5ms). // - Workers don't have DOM access; serialize data, not functions.