JavaScript is single-threaded, but Web Workers let you run scripts in background threads. Keep your UI responsive while handling heavy computations.
The Problem#
1// This blocks the UI
2function fibonacci(n) {
3 if (n <= 1) return n;
4 return fibonacci(n - 1) + fibonacci(n - 2);
5}
6
7// UI freezes during calculation
8const result = fibonacci(45);
9console.log(result);Dedicated Workers#
Creating a Worker#
1// main.js
2const worker = new Worker('worker.js');
3
4// Send message to worker
5worker.postMessage({ type: 'fibonacci', n: 45 });
6
7// Receive results
8worker.onmessage = (event) => {
9 console.log('Result:', event.data.result);
10};
11
12worker.onerror = (error) => {
13 console.error('Worker error:', error.message);
14};1// worker.js
2function fibonacci(n) {
3 if (n <= 1) return n;
4 return fibonacci(n - 1) + fibonacci(n - 2);
5}
6
7self.onmessage = (event) => {
8 const { type, n } = event.data;
9
10 if (type === 'fibonacci') {
11 const result = fibonacci(n);
12 self.postMessage({ result });
13 }
14};Inline Workers#
1// Create worker from blob
2function createInlineWorker(fn) {
3 const blob = new Blob([`(${fn.toString()})()`], {
4 type: 'application/javascript',
5 });
6 return new Worker(URL.createObjectURL(blob));
7}
8
9const worker = createInlineWorker(() => {
10 self.onmessage = (e) => {
11 const result = e.data * 2;
12 self.postMessage(result);
13 };
14});
15
16worker.postMessage(21);
17worker.onmessage = (e) => console.log(e.data); // 42Transferable Objects#
1// Transferring data (zero-copy)
2const buffer = new ArrayBuffer(1024 * 1024); // 1MB
3
4// Transfer ownership (fast, but buffer becomes unusable in main thread)
5worker.postMessage({ buffer }, [buffer]);
6console.log(buffer.byteLength); // 0 - transferred
7
8// In worker
9self.onmessage = (event) => {
10 const { buffer } = event.data;
11 const view = new Uint8Array(buffer);
12
13 // Process data
14 for (let i = 0; i < view.length; i++) {
15 view[i] = view[i] * 2;
16 }
17
18 // Transfer back
19 self.postMessage({ buffer }, [buffer]);
20};Worker Pool#
1class WorkerPool {
2 private workers: Worker[] = [];
3 private queue: Array<{
4 data: any;
5 resolve: (value: any) => void;
6 reject: (error: any) => void;
7 }> = [];
8 private availableWorkers: Worker[] = [];
9
10 constructor(workerScript: string, poolSize: number = navigator.hardwareConcurrency) {
11 for (let i = 0; i < poolSize; i++) {
12 const worker = new Worker(workerScript);
13 worker.onmessage = (event) => this.handleMessage(worker, event);
14 worker.onerror = (error) => this.handleError(worker, error);
15 this.workers.push(worker);
16 this.availableWorkers.push(worker);
17 }
18 }
19
20 execute<T>(data: any): Promise<T> {
21 return new Promise((resolve, reject) => {
22 const worker = this.availableWorkers.pop();
23
24 if (worker) {
25 this.runTask(worker, data, resolve, reject);
26 } else {
27 this.queue.push({ data, resolve, reject });
28 }
29 });
30 }
31
32 private runTask(
33 worker: Worker,
34 data: any,
35 resolve: (value: any) => void,
36 reject: (error: any) => void
37 ) {
38 (worker as any)._resolve = resolve;
39 (worker as any)._reject = reject;
40 worker.postMessage(data);
41 }
42
43 private handleMessage(worker: Worker, event: MessageEvent) {
44 const resolve = (worker as any)._resolve;
45 resolve(event.data);
46 this.releaseWorker(worker);
47 }
48
49 private handleError(worker: Worker, error: ErrorEvent) {
50 const reject = (worker as any)._reject;
51 reject(error);
52 this.releaseWorker(worker);
53 }
54
55 private releaseWorker(worker: Worker) {
56 const next = this.queue.shift();
57 if (next) {
58 this.runTask(worker, next.data, next.resolve, next.reject);
59 } else {
60 this.availableWorkers.push(worker);
61 }
62 }
63
64 terminate() {
65 this.workers.forEach((worker) => worker.terminate());
66 }
67}
68
69// Usage
70const pool = new WorkerPool('compute-worker.js', 4);
71
72const results = await Promise.all([
73 pool.execute({ task: 'hash', data: 'password1' }),
74 pool.execute({ task: 'hash', data: 'password2' }),
75 pool.execute({ task: 'hash', data: 'password3' }),
76]);Shared Workers#
1// Shared across tabs/windows
2// shared-worker.js
3const connections = [];
4
5self.onconnect = (event) => {
6 const port = event.ports[0];
7 connections.push(port);
8
9 port.onmessage = (e) => {
10 // Broadcast to all connections
11 connections.forEach((conn) => {
12 conn.postMessage({
13 from: connections.indexOf(port),
14 message: e.data,
15 });
16 });
17 };
18
19 port.start();
20};1// main.js (in multiple tabs)
2const worker = new SharedWorker('shared-worker.js');
3
4worker.port.onmessage = (event) => {
5 console.log('Received:', event.data);
6};
7
8worker.port.start();
9worker.port.postMessage('Hello from this tab');Comlink (Simplified API)#
1// With Comlink library
2// worker.js
3import * as Comlink from 'comlink';
4
5const api = {
6 async processImage(imageData) {
7 // Heavy processing
8 return processedData;
9 },
10
11 async calculateStats(data) {
12 return {
13 mean: data.reduce((a, b) => a + b) / data.length,
14 // ... more calculations
15 };
16 },
17};
18
19Comlink.expose(api);1// main.js
2import * as Comlink from 'comlink';
3
4const worker = new Worker('worker.js');
5const api = Comlink.wrap(worker);
6
7// Call worker methods directly
8const stats = await api.calculateStats([1, 2, 3, 4, 5]);
9console.log(stats.mean);Use Cases#
Good for Workers:
✓ Image/video processing
✓ Data parsing (CSV, JSON)
✓ Cryptographic operations
✓ Complex calculations
✓ Compression/decompression
✓ Syntax highlighting
Not for Workers:
✗ DOM manipulation
✗ Simple calculations
✗ Tasks requiring window/document
✗ Short-lived operations
Limitations#
1// No access to:
2// - window
3// - document
4// - parent
5// - DOM APIs
6
7// Available in workers:
8// - fetch()
9// - IndexedDB
10// - WebSockets
11// - setTimeout/setInterval
12// - importScripts()
13// - navigator (partial)
14// - location (read-only)Conclusion#
Web Workers enable true parallelism in JavaScript. Use them for computationally intensive tasks to keep your UI responsive.
The key is identifying operations that don't need DOM access and can benefit from parallel execution.