HTML5 Web Workers

Web Workers run JavaScript in background threads, preventing heavy computations from blocking the user interface. They enable true multi-threading in web applications.

Creating a Web Worker

Web Workers run JavaScript in a separate thread from the main UI thread.

Create a worker by passing a JavaScript file path to the Worker constructor.

Workers communicate with the main thread via messages using postMessage().

Workers cannot access the DOM or window object directly.

Use workers for CPU-intensive tasks like data processing, encryption, or complex calculations.

<!-- Main HTML file -->
<button onclick="startWorker()">Start Worker</button>
<button onclick="stopWorker()">Stop Worker</button>
<p id="result"></p>

<script>
let worker;

function startWorker() {
  if (typeof Worker !== 'undefined') {
    if (!worker) {
      worker = new Worker('worker.js');
    }
    
    worker.onmessage = function(event) {
      document.getElementById('result').textContent = 
        'Worker says: ' + event.data;
    };
    
    worker.postMessage('Hello Worker!');
  } else {
    document.getElementById('result').textContent = 
      'Web Workers not supported.';
  }
}

function stopWorker() {
  if (worker) {
    worker.terminate();
    worker = undefined;
  }
}
</script>

<!-- worker.js file -->
<script>
// This code runs in the worker
onmessage = function(event) {
  console.log('Worker received:', event.data);
  
  // Perform heavy computation
  let result = performHeavyTask();
  
  // Send result back to main thread
  postMessage(result);
};

function performHeavyTask() {
  let count = 0;
  for (let i = 0; i < 1000000000; i++) {
    count += i;
  }
  return 'Task complete! Count: ' + count;
}
</script>

Worker Communication

Use postMessage() to send data to/from workers.

Listen for messages with the onmessage event handler.

Data is copied (structured clone), not shared - changes don't affect the original.

Transferable objects (ArrayBuffers) can be transferred without copying for better performance.

Handle errors with the onerror event handler.

<!-- Main thread -->
<button onclick="calculatePrimes()">Calculate Primes</button>
<p id="status">Ready</p>
<p id="primes"></p>

<script>
const worker = new Worker('primes-worker.js');

worker.onmessage = function(event) {
  if (event.data.type === 'progress') {
    document.getElementById('status').textContent = 
      `Progress: ${event.data.percent}%`;
  } else if (event.data.type === 'complete') {
    document.getElementById('status').textContent = 'Complete!';
    document.getElementById('primes').textContent = 
      `Found ${event.data.primes.length} primes`;
  }
};

worker.onerror = function(error) {
  console.error('Worker error:', error.message);
  document.getElementById('status').textContent = 'Error in worker';
};

function calculatePrimes() {
  document.getElementById('status').textContent = 'Calculating...';
  worker.postMessage({ max: 100000 });
}
</script>

<!-- primes-worker.js -->
<script>
onmessage = function(event) {
  const max = event.data.max;
  const primes = [];
  
  for (let n = 2; n <= max; n++) {
    let isPrime = true;
    for (let i = 2; i <= Math.sqrt(n); i++) {
      if (n % i === 0) {
        isPrime = false;
        break;
      }
    }
    if (isPrime) primes.push(n);
    
    // Report progress every 10000 numbers
    if (n % 10000 === 0) {
      postMessage({ type: 'progress', percent: Math.floor(n / max * 100) });
    }
  }
  
  postMessage({ type: 'complete', primes: primes });
};
</script>

Worker Limitations

Workers cannot access the DOM - no document, window, or parent objects.

Workers can use: navigator, location (read-only), XMLHttpRequest, setTimeout/setInterval.

Workers can create new workers (sub-workers).

Workers can import scripts using importScripts().

Shared Workers can be accessed by multiple scripts; Dedicated Workers are tied to one script.

<!-- What workers CAN do -->
<!-- Inside worker.js -->
<script>
// Import external scripts
importScripts('utils.js', 'library.js');

// Make HTTP requests
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => postMessage(data));

// Use timers
setTimeout(function() {
  postMessage('Delayed message');
}, 1000);

// Access navigator and location
const userAgent = navigator.userAgent;
const url = location.href;

// Create sub-workers
const subWorker = new Worker('sub-worker.js');
</script>

<!-- What workers CANNOT do -->
<script>
// These will fail in a worker:
// document.getElementById('test');  // Error: document is not defined
// window.alert('test');             // Error: window is not defined  
// parent.postMessage('test');       // Error: parent is not defined
</script>

Practical Use Cases

Image processing: Filters, compression, format conversion.

Data processing: Sorting, filtering, searching large datasets.

Encryption/decryption: Cryptographic operations without UI freeze.

Game logic: Physics calculations, AI pathfinding.

Real-time data analysis: Processing streaming data or sensor inputs.

Background sync: Fetching and processing data while user interacts with UI.

<!-- Image processing example -->
<input type="file" id="imageInput" accept="image/*">
<canvas id="canvas"></canvas>
<p id="status">Upload an image</p>

<script>
const worker = new Worker('image-worker.js');

document.getElementById('imageInput').addEventListener('change', function(e) {
  const file = e.target.files[0];
  const reader = new FileReader();
  
  reader.onload = function(event) {
    const img = new Image();
    img.onload = function() {
      const canvas = document.getElementById('canvas');
      const ctx = canvas.getContext('2d');
      canvas.width = img.width;
      canvas.height = img.height;
      ctx.drawImage(img, 0, 0);
      
      // Get image data and send to worker
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
      document.getElementById('status').textContent = 'Processing...';
      worker.postMessage(imageData);
    };
    img.src = event.target.result;
  };
  
  reader.readAsDataURL(file);
});

worker.onmessage = function(event) {
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');
  ctx.putImageData(event.data, 0, 0);
  document.getElementById('status').textContent = 'Grayscale applied!';
};
</script>

<!-- image-worker.js -->
<script>
onmessage = function(event) {
  const imageData = event.data;
  const data = imageData.data;
  
  // Convert to grayscale
  for (let i = 0; i < data.length; i += 4) {
    const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
    data[i] = avg;     // Red
    data[i + 1] = avg; // Green
    data[i + 2] = avg; // Blue
  }
  
  postMessage(imageData);
};
</script>