⚙️

How to Use Web Workers

JavaScriptAdvanced

Run scripts in background threads.

Code Example

JavaScript
// Main thread - Create worker
const worker = new Worker('worker.js');

// Send message to worker
worker.postMessage({ type: 'calculate', data: 1000000 });

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

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

// Terminate worker
// worker.terminate();

// worker.js (separate file)
/*
self.onmessage = (event) => {
  const { type, data } = event.data;
  
  if (type === 'calculate') {
    // Heavy computation
    let sum = 0;
    for (let i = 0; i < data; i++) {
      sum += i;
    }
    
    // Send result back
    self.postMessage({ result: sum });
  }
};
*/

// Inline worker (Blob URL)
const workerCode = `
  self.onmessage = (e) => {
    const result = e.data * 2;
    self.postMessage(result);
  };
`;

const blob = new Blob([workerCode], { type: 'application/javascript' });
const inlineWorker = new Worker(URL.createObjectURL(blob));

inlineWorker.postMessage(21);
inlineWorker.onmessage = (e) => {
  console.log('Inline worker result:', e.data); // 42
};

// Shared worker (multiple tabs)
const sharedWorker = new SharedWorker('shared-worker.js');
sharedWorker.port.onmessage = (e) => {
  console.log('Shared worker:', e.data);
};
sharedWorker.port.start();

// Practical example: Image processing
const imageWorker = new Worker('image-processor.js');

function processImage(imageData) {
  return new Promise((resolve) => {
    imageWorker.onmessage = (e) => {
      resolve(e.data);
    };
    imageWorker.postMessage(imageData);
  });
}

// Practical example: Debounced search
let searchWorker;

function initSearchWorker() {
  searchWorker = new Worker('search-worker.js');
  searchWorker.onmessage = (e) => {
    displayResults(e.data);
  };
}

function search(query) {
  searchWorker.postMessage({ query });
}

// Transferable objects (zero-copy)
const buffer = new ArrayBuffer(1024);
worker.postMessage(buffer, [buffer]);
// buffer is now detached, cannot be used in main thread

// Worker pools
class WorkerPool {
  constructor(workerScript, poolSize) {
    this.workers = [];
    this.queue = [];
    
    for (let i = 0; i < poolSize; i++) {
      const worker = new Worker(workerScript);
      worker.onmessage = (e) => {
        this.handleWorkerMessage(worker, e.data);
      };
      this.workers.push({ worker, busy: false });
    }
  }
  
  runTask(data) {
    return new Promise((resolve) => {
      const availableWorker = this.workers.find(w => !w.busy);
      if (availableWorker) {
        availableWorker.busy = true;
        availableWorker.resolve = resolve;
        availableWorker.worker.postMessage(data);
      } else {
        this.queue.push({ data, resolve });
      }
    });
  }
  
  handleWorkerMessage(workerObj, result) {
    workerObj.busy = false;
    workerObj.resolve(result);
    
    if (this.queue.length > 0) {
      const { data, resolve } = this.queue.shift();
      workerObj.busy = true;
      workerObj.resolve = resolve;
      workerObj.worker.postMessage(data);
    }
  }
}

Explanation

Web Workers run scripts in background threads. Use postMessage() to communicate. Inline workers with Blob URLs. Transferable objects for zero-copy. Worker pools for task distribution.

Related Tutorials