Back to Blog
Performance2026-04-05

Web Workers: Offload Heavy Tasks for Better Performance

Learn how to use Web Workers to run CPU-intensive tasks off the main thread and keep your UI responsive.

Why Web Workers?

JavaScript runs on a single thread. When you perform heavy computation — image processing, data parsing, complex calculations — the main thread freezes and your UI becomes unresponsive. Web Workers let you spin up background threads that run JavaScript without blocking the UI.

Workers communicate with the main thread via message passing. They cannot access the DOM directly, but they can perform network requests, use IndexedDB, and run any pure computation.

Basic Worker Setup

``javascript // main.js const worker = new Worker('worker.js');

worker.postMessage({ type: 'calculate', data: [1, 2, 3, 4, 5] });

worker.onmessage = (event) => { console.log('Result from worker:', event.data); };

worker.onerror = (error) => { console.error('Worker error:', error.message); };

// worker.js self.onmessage = (event) => { const { type, data } = event.data;

if (type === 'calculate') { // Heavy computation runs here, off the main thread const result = data.reduce((sum, n) => { // Simulate expensive work for (let i = 0; i < 1e7; i++) {} return sum + n * n; }, 0);

self.postMessage(result); } }; `

Inline Workers with Blob URLs

You don't always need separate files. Create workers inline using Blob URLs — great for bundled applications.

`javascript function createInlineWorker(fn) { const blob = new Blob( [\self.onmessage = \${fn.toString()}\], { type: 'application/javascript' } ); return new Worker(URL.createObjectURL(blob)); }

const worker = createInlineWorker((event) => { const numbers = event.data; const sorted = [...numbers].sort((a, b) => a - b); self.postMessage(sorted); });

worker.postMessage([5, 3, 8, 1, 9]); worker.onmessage = (e) => console.log(e.data); // [1, 3, 5, 8, 9] `

Transferable Objects for Zero-Copy Performance

When sending large data (ArrayBuffers, ImageData), use transferable objects to avoid copying — the data is moved, not cloned.

`javascript // Main thread — transfer an ArrayBuffer const buffer = new ArrayBuffer(1024 * 1024); // 1MB worker.postMessage(buffer, [buffer]); // buffer.byteLength is now 0 — ownership transferred

// Worker — process and transfer back self.onmessage = (event) => { const buffer = event.data; const view = new Float64Array(buffer); for (let i = 0; i < view.length; i++) { view[i] = Math.sqrt(view[i]); } self.postMessage(buffer, [buffer]); }; `

Worker Pool Pattern

For repeated tasks, reuse workers instead of creating new ones each time.

`javascript class WorkerPool { constructor(script, size = navigator.hardwareConcurrency || 4) { this.workers = Array.from({ length: size }, () => new Worker(script)); this.queue = []; this.available = [...this.workers]; }

run(data) { return new Promise((resolve) => { const task = { data, resolve }; const worker = this.available.pop(); if (worker) { this.dispatch(worker, task); } else { this.queue.push(task); } }); }

dispatch(worker, task) { worker.onmessage = (e) => { task.resolve(e.data); const next = this.queue.shift(); if (next) this.dispatch(worker, next); else this.available.push(worker); }; worker.postMessage(task.data); } } ``

Web Workers are essential for any performance-sensitive web application. Use them for image processing, CSV parsing, encryption, or any work that would otherwise block the main thread.