👷

Web Workers

advanced

Web Workers enable multi-threading in JavaScript by running scripts in background threads. This keeps the main thread free for UI updates, preventing the page from freezing during heavy computations.

🎮Interactive Visualization

Code
1// main.js
2const worker = new Worker('worker.js');
3
4worker.postMessage('Hello!');
5
6worker.onmessage = (e) => {
7 console.log('Reply:', e.data);
8};
9
10// worker.js
11onmessage = (e) => {
12 postMessage('Got: ' + e.data);
13};
Main Threadbusy
Creating worker
Step 1/5Creating a new Web Worker spawns a separate JavaScript thread
💡 Key Insight: Web Workers run JavaScript in background threads, keeping the main thread responsive for UI updates.

Key Points

  • Workers run in separate threads (true parallelism)
  • Main thread stays responsive during heavy computation
  • Communication via postMessage (data is copied, not shared)
  • Workers have no DOM access (no document, window, etc.)
  • SharedArrayBuffer allows shared memory between threads
  • Service Workers: special workers for offline caching and network interception

💻Code Examples

Basic Worker

// main.js
const worker = new Worker("worker.js");

// Send data to worker
worker.postMessage({ numbers: [1, 2, 3, 4, 5] });

// Receive result from worker
worker.onmessage = (e) => {
  console.log("Sum:", e.data.sum);
};

// worker.js
self.onmessage = (e) => {
  const sum = e.data.numbers.reduce((a, b) => a + b);
  self.postMessage({ sum: sum });
};

Worker runs in background thread

Heavy Computation

// Without worker: UI freezes!
function fibonacci(n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}
fibonacci(45);  // Blocks for seconds!

// With worker: UI stays responsive
const worker = new Worker("fib-worker.js");
worker.postMessage(45);
worker.onmessage = (e) => {
  console.log("Result:", e.data);
};

// User can still click buttons!

Offload heavy work to keep UI responsive

Transferable Objects

// postMessage copies data (slow for large data)

const bigArray = new Float32Array(1000000);

// SLOW: Data is copied
worker.postMessage(bigArray);

// FAST: Transfer ownership (zero-copy)
worker.postMessage(bigArray, [bigArray.buffer]);

// After transfer, bigArray is empty!
console.log(bigArray.length);  // 0

// Worker now owns the buffer

Transfer large data without copying

SharedArrayBuffer

// Create shared memory
const shared = new SharedArrayBuffer(4);
const view = new Int32Array(shared);

// Pass to worker (no copy!)
worker.postMessage(shared);

// Both threads see same memory
view[0] = 42;  // Worker sees this!

// Use Atomics for thread-safe access
Atomics.add(view, 0, 1);
Atomics.load(view, 0);  // 43

// Note: Requires COOP/COEP headers

SharedArrayBuffer for shared memory

Worker Pool

class WorkerPool {
  constructor(size, script) {
    this.workers = Array(size).fill(null)
      .map(() => new Worker(script));
    this.queue = [];
    this.available = [...this.workers];
  }

  run(data) {
    return new Promise((resolve) => {
      const task = { data, resolve };

      if (this.available.length > 0) {
        this.dispatch(task);
      } else {
        this.queue.push(task);
      }
    });
  }

  dispatch(task) {
    const worker = this.available.pop();
    worker.onmessage = (e) => {
      task.resolve(e.data);
      this.available.push(worker);
      if (this.queue.length > 0) {
        this.dispatch(this.queue.shift());
      }
    };
    worker.postMessage(task.data);
  }
}

Pool workers for efficient parallel processing

Service Worker

// Register service worker
navigator.serviceWorker.register("/sw.js");

// sw.js - Intercept network requests
self.addEventListener("fetch", (e) => {
  e.respondWith(
    caches.match(e.request).then((cached) => {
      // Return cached version or fetch
      return cached || fetch(e.request);
    })
  );
});

// Cache resources for offline use
self.addEventListener("install", (e) => {
  e.waitUntil(
    caches.open("v1").then((cache) => {
      return cache.addAll(["/", "/app.js"]);
    })
  );
});

Service Workers enable offline-first apps

Common Mistakes

  • Trying to access DOM from a worker (not allowed)
  • Sending large data without using Transferable objects
  • Not handling worker errors (silent failures)
  • Creating too many workers (overhead)

Interview Tips

  • Explain when to use Web Workers vs main thread
  • Know the difference between copying and transferring data
  • Understand SharedArrayBuffer and Atomics for thread safety
  • Explain Service Workers and their use cases (PWA, offline)

🔗Related Concepts