HTML5 Mastery: The Complete Web Foundation
HomeInsightsCoursesHTMLWeb Workers Introduction
HTML5 APIs

Web Workers API

Run JavaScript in background threads with Web Workers. Keep your UI responsive while performing heavy computations, processing large datasets, or handling complex calculations.

What are Web Workers?

Web Workers allow you to run JavaScript in background threads, separate from the main UI thread. This prevents long-running scripts from freezing the page.

âš¡ Non-Blocking

Heavy tasks don't freeze UI

🚀 Performance

Utilize multiple CPU cores

🔄 Parallel Processing

Run multiple workers simultaneously

📊 Data Processing

Perfect for large datasets

Basic Web Worker Example

Main Script (index.html)

HTML
<!DOCTYPE html>
<html>
<head>
    <title>Web Worker Demo</title>
</head>
<body>
    <button onclick="startWorker()">Start Worker</button>
    <button onclick="stopWorker()">Stop Worker</button>
    <div id="result"></div>

    <script>
    let worker;

    function startWorker() {
        // Create worker from external file
        worker = new Worker('worker.js');
        
        // Listen for messages from worker
        worker.onmessage = function(e) {
            document.getElementById('result').textContent = 
                'Worker says: ' + e.data;
        };
        
        // Handle errors
        worker.onerror = function(error) {
            console.error('Worker error:', error.message);
        };
        
        // Send message to worker
        worker.postMessage('Hello Worker!');
    }

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

Worker Script (worker.js)

JAVASCRIPT
// worker.js - Runs in separate thread

// Listen for messages from main thread
self.onmessage = function(e) {
    console.log('Worker received:', e.data);
    
    // Do some work
    const result = 'Processed: ' + e.data;
    
    // Send result back to main thread
    self.postMessage(result);
};

// Or use addEventListener
self.addEventListener('message', function(e) {
    // Handle message
});

Practical Example: Heavy Computation

Main Script

HTML
<!DOCTYPE html>
<html>
<head>
    <title>Prime Number Calculator</title>
</head>
<body>
    <h1>Prime Number Calculator (with Web Worker)</h1>
    <input type="number" id="max" value="100000" min="1000">
    <button onclick="calculatePrimes()">Calculate Primes</button>
    <button onclick="testUI()">Test UI (Click Me)</button>
    <div id="status">Ready</div>
    <div id="result"></div>

    <script>
    let worker;

    function calculatePrimes() {
        const max = document.getElementById('max').value;
        const status = document.getElementById('status');
        
        status.textContent = 'Calculating...';
        
        // Create worker
        worker = new Worker('prime-worker.js');
        
        worker.onmessage = function(e) {
            const primes = e.data;
            status.textContent = 'Done!';
            document.getElementById('result').textContent = 
                `Found ${primes.length} prime numbers`;
            worker.terminate();
        };
        
        // Send data to worker
        worker.postMessage({ max: parseInt(max) });
    }

    function testUI() {
        // This should work smoothly even during calculation
        alert('UI is responsive!');
    }
    </script>
</body>
</html>

Worker Script (prime-worker.js)

JAVASCRIPT
// prime-worker.js
self.onmessage = function(e) {
    const max = e.data.max;
    const primes = [];
    
    // Heavy computation (blocking in main thread, but OK in worker)
    for (let num = 2; num <= max; num++) {
        let isPrime = true;
        for (let i = 2; i <= Math.sqrt(num); i++) {
            if (num % i === 0) {
                isPrime = false;
                break;
            }
        }
        if (isPrime) {
            primes.push(num);
        }
        
        // Optional: Send progress updates
        if (num % 10000 === 0) {
            self.postMessage({ 
                type: 'progress', 
                current: num, 
                max: max 
            });
        }
    }
    
    // Send final result
    self.postMessage(primes);
};

Inline Workers

Create workers without separate files using Blob URLs:

JAVASCRIPT
// Create worker code as string
const workerCode = `
    self.onmessage = function(e) {
        const result = e.data * 2;
        self.postMessage(result);
    };
`;

// Create blob from code
const blob = new Blob([workerCode], { type: 'application/javascript' });

// Create object URL
const workerUrl = URL.createObjectURL(blob);

// Create worker
const worker = new Worker(workerUrl);

worker.onmessage = function(e) {
    console.log('Result:', e.data);
};

worker.postMessage(10); // Result: 20

// Clean up when done
worker.terminate();
URL.revokeObjectURL(workerUrl);

Transferring Data

Structured Cloning (Default)

JAVASCRIPT
// Main thread
const data = {
    numbers: [1, 2, 3, 4, 5],
    text: 'Hello',
    nested: { value: 42 }
};

// Data is cloned (copied) to worker
worker.postMessage(data);

// Original data unchanged
console.log(data); // Still accessible

Transferable Objects (Fast)

For large data (ArrayBuffers), transfer ownership instead of copying:

JAVASCRIPT
// Main thread
const buffer = new ArrayBuffer(1024 * 1024); // 1MB
const array = new Uint8Array(buffer);

// Fill with data
for (let i = 0; i < array.length; i++) {
    array[i] = i % 256;
}

// Transfer buffer (zero-copy, very fast)
worker.postMessage({ buffer: buffer }, [buffer]);

// ⚠️ buffer is now transferred - can't use in main thread
console.log(buffer.byteLength); // 0 (detached)

// Worker receives the buffer
// worker.js
self.onmessage = function(e) {
    const buffer = e.data.buffer;
    console.log('Received:', buffer.byteLength); // 1048576
    
    // Process data...
    
    // Transfer back when done
    self.postMessage({ buffer: buffer }, [buffer]);
};

Worker Scope and Limitations

✅ Available in Workers

  • self (global object)
  • navigator
  • location (read-only)
  • XMLHttpRequest / fetch()
  • setTimeout() / setInterval()
  • importScripts() (load external scripts)
  • Most JavaScript built-ins (Array, Object, Math, etc.)

❌ NOT Available in Workers

  • window
  • document
  • parent
  • DOM manipulation
  • localStorage / sessionStorage
  • Direct UI updates
JAVASCRIPT
// worker.js

// ✅ This works
self.postMessage('Hello');
fetch('https://api.example.com/data');
const array = [1, 2, 3];

// ❌ This doesn't work
document.getElementById('result'); // Error: document is not defined
window.alert('Hi'); // Error: window is not defined
localStorage.setItem('key', 'value'); // Error: localStorage is not defined

Importing Scripts in Workers

JAVASCRIPT
// worker.js

// Import external scripts
importScripts('utils.js');
importScripts('math.js', 'helpers.js'); // Multiple files

// Now can use functions from imported scripts
self.onmessage = function(e) {
    // Use function from utils.js
    const result = processData(e.data);
    self.postMessage(result);
};

Example: Image Processing

HTML
<!DOCTYPE html>
<html>
<head>
    <title>Image Processing with Worker</title>
</head>
<body>
    <input type="file" id="imageInput" accept="image/*">
    <canvas id="canvas"></canvas>
    <div id="status"></div>

    <script>
    const imageInput = document.getElementById('imageInput');
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    const status = document.getElementById('status');

    imageInput.onchange = function(e) {
        const file = e.target.files[0];
        const reader = new FileReader();
        
        reader.onload = function(event) {
            const img = new Image();
            img.onload = function() {
                canvas.width = img.width;
                canvas.height = img.height;
                ctx.drawImage(img, 0, 0);
                
                // Get image data
                const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
                
                // Process in worker
                status.textContent = 'Processing...';
                const worker = new Worker('image-worker.js');
                
                worker.onmessage = function(e) {
                    // Put processed image back
                    ctx.putImageData(e.data, 0, 0);
                    status.textContent = 'Done!';
                    worker.terminate();
                };
                
                // Send image data to worker
                worker.postMessage({ imageData: imageData });
            };
            img.src = event.target.result;
        };
        reader.readAsDataURL(file);
    };
    </script>
</body>
</html>

Worker (image-worker.js)

JAVASCRIPT
// image-worker.js
self.onmessage = function(e) {
    const imageData = e.data.imageData;
    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
        // data[i + 3] is alpha, leave unchanged
    }
    
    // Send processed image back
    self.postMessage(imageData);
};

Shared Workers

Shared Workers can be accessed by multiple scripts (tabs, iframes) from the same origin:

JAVASCRIPT
// Main script (can be in multiple tabs)
const sharedWorker = new SharedWorker('shared-worker.js');

sharedWorker.port.onmessage = function(e) {
    console.log('Message from shared worker:', e.data);
};

sharedWorker.port.postMessage('Hello from tab');

// shared-worker.js
const connections = [];

self.onconnect = function(e) {
    const port = e.ports[0];
    connections.push(port);
    
    port.onmessage = function(e) {
        // Broadcast to all connected tabs
        connections.forEach(conn => {
            conn.postMessage('Message: ' + e.data);
        });
    };
    
    port.start();
};

Best Practices

✅ Good Use Cases

  • Heavy computations (math, crypto)
  • Large dataset processing
  • Image/video processing
  • Real-time data analysis
  • Background sync
  • Parsing large JSON/XML

❌ Poor Use Cases

  • Simple, quick operations
  • DOM manipulation
  • Operations needing window/document
  • Very frequent small messages (overhead)

Performance Tips

  • ✅ Reuse workers instead of creating new ones
  • ✅ Use transferable objects for large data
  • ✅ Terminate workers when done
  • ✅ Send progress updates for long operations
  • ❌ Don't create too many workers (overhead)
  • ❌ Don't send UI objects to workers

Browser Support

Web Workers are supported in all modern browsers:

  • ✅ Chrome 4+
  • ✅ Firefox 3.5+
  • ✅ Safari 4+
  • ✅ Edge (all versions)
  • ✅ iOS Safari 5+
  • ✅ Android 4.4+

Feature Detection

JAVASCRIPT
if (typeof Worker !== 'undefined') {
    // Web Workers supported
    const worker = new Worker('worker.js');
} else {
    // Fallback: run in main thread
    console.warn('Web Workers not supported');
}

What's Next?

Learn about responsive web design and mobile optimization techniques.