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 accessibleTransferable 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)navigatorlocation(read-only)XMLHttpRequest/fetch()setTimeout()/setInterval()importScripts()(load external scripts)- Most JavaScript built-ins (Array, Object, Math, etc.)
⌠NOT Available in Workers
windowdocumentparent- 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 definedImporting 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');
}