JavaScript Web Workers

Web Workers allow you to run JavaScript in background threads, separate from the main thread. This prevents heavy computations from blocking the user interface, keeping the page responsive.

What Are Web Workers?

A Web Worker is a JavaScript script that runs in a background thread. Workers can perform complex calculations, process data, or handle network requests without freezing the main UI thread.

Why Web Workers Matter
// Without workers: heavy computation blocks the UI
function heavyComputation() {
  let sum = 0;
  for (let i = 0; i < 1000000; i++) {
    sum += Math.sqrt(i);
  }
  return sum;
}

console.log('Starting computation...');
const result = heavyComputation();
console.log('Result: ' + result.toFixed(2));
console.log('This was blocking - UI would freeze!');

// With workers, this runs in a background thread
// and the UI stays responsive

Creating a Worker

To create a worker, you pass the URL of a JavaScript file to the Worker constructor. The worker file runs in a separate thread.

Creating and Using a Worker
// Main script (app.js)
// const worker = new Worker('worker.js');

// Worker file (worker.js):
// self.onmessage = function(e) {
//   const result = e.data * 2;
//   self.postMessage(result);
// };

// Simulating worker communication
console.log('Main: Creating worker...');
console.log('Main: Sending data to worker');

// Worker would process in background
const workerInput = 42;
const workerOutput = workerInput * 2;

console.log('Main: Sent value: ' + workerInput);
console.log('Worker: Received and processed');
console.log('Main: Got result: ' + workerOutput);

postMessage() and onmessage

Communication between the main thread and workers is done using postMessage() to send data and onmessage to receive data. Data is copied (structured cloning), not shared.

Message Passing
// Main thread sends data with postMessage()
// worker.postMessage({ task: 'calculate', data: [1,2,3] });

// Worker receives with onmessage
// self.onmessage = function(event) {
//   const { task, data } = event.data;
//   if (task === 'calculate') {
//     const sum = data.reduce((a, b) => a + b, 0);
//     self.postMessage({ result: sum });
//   }
// };

// Main thread receives worker's response
// worker.onmessage = function(event) {
//   console.log('Result: ' + event.data.result);
// };

// Simulating the full exchange
const message = { task: 'calculate', data: [1, 2, 3, 4, 5] };
console.log('Main -> Worker: ' + JSON.stringify(message));

const sum = message.data.reduce((a, b) => a + b, 0);
const response = { result: sum };
console.log('Worker -> Main: ' + JSON.stringify(response));
console.log('Sum: ' + response.result);

Terminating Workers

Workers can be terminated from the main thread using worker.terminate() or from within the worker using self.close(). Terminated workers cannot be restarted.

Terminating a Worker
// From main thread: immediate termination
// worker.terminate();

// From inside the worker: graceful shutdown
// self.close();

// Error handling
// worker.onerror = function(error) {
//   console.log('Worker error:', error.message);
//   console.log('File:', error.filename);
//   console.log('Line:', error.lineno);
// };

console.log('worker.terminate() - kills from main thread');
console.log('self.close() - worker shuts itself down');
console.log('worker.onerror - handles worker errors');

// Best practice: send a shutdown message
// worker.postMessage({ type: 'shutdown' });
// In worker:
// if (e.data.type === 'shutdown') { self.close(); }
console.log('Best practice: send shutdown message first');

Worker Limitations and Use Cases

Workers run in an isolated environment with some important limitations. They cannot access the DOM, window, or document objects. However, they can use many Web APIs.

Available in WorkersNOT Available in Workers
XMLHttpRequest / fetchDOM (document, window)
setTimeout / setIntervaldocument.querySelector()
JSON, Math, Arrayalert(), confirm(), prompt()
WebSocketsParent page variables
IndexedDBlocalStorage / sessionStorage
importScripts()UI manipulation
Practical Use Cases
// Use cases for Web Workers:

// 1. Heavy calculations
function fibonacci(n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}
console.log('Fibonacci(20) = ' + fibonacci(20));

// 2. Data processing
const data = Array.from({length: 1000}, (_, i) => i);
const processed = data.filter(n => n % 2 === 0).map(n => n * n);
console.log('Processed items: ' + processed.length);

// 3. Image processing, encryption, sorting
// 4. Polling APIs in the background
// 5. Parsing large JSON files
console.log('Workers keep the UI thread free!');
📝 Note: Data passed between the main thread and workers is copied via the structured clone algorithm, not shared. For large data transfers, you can use Transferable objects (like ArrayBuffer) which transfer ownership instead of copying, providing much better performance.
Exercise:
What can Web Workers NOT access?
Try it YourselfCtrl+Enter to run
Click Run to see the output here.