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>