HTML Web Workers
Learn how to run JavaScript in background threads without blocking the UI
Web Workers allow you to run JavaScript code in background threads, enabling heavy computations without freezing the user interface. This makes web applications more responsive and improves performance.
What are Web Workers?
Web Workers are JavaScript scripts that run in the background, independently of the main execution thread. They don't have access to the DOM but can perform computationally intensive tasks without blocking the UI.
Key Benefits
- Non-Blocking: UI remains responsive during heavy operations
- Multi-Threading: True parallel execution in browsers
- Performance: Better utilization of multi-core processors
- Isolated Scope: Workers run in separate global context
Types of Web Workers
Checking for Web Worker Support
Always check if Web Workers are supported before using them:
Feature Detection
if (typeof(Worker) !== "undefined") {
// Web Workers are supported
console.log("Web Workers are available");
} else {
// Web Workers are not supported
console.log("Sorry, Web Workers are not supported");
}
Creating a Web Worker
To create a Web Worker, you need two files: the main script and the worker script.
Main Script (index.html)
<p>Count: <span id="result"></span></p>
<button onclick="startWorker()">Start Worker</button>
<button onclick="stopWorker()">Stop Worker</button>
<script>
var worker;
function startWorker() {
if (typeof(Worker) !== "undefined") {
if (typeof(worker) == "undefined") {
// Create new worker
worker = new Worker("worker.js");
}
// Listen for messages from worker
worker.onmessage = function(event) {
document.getElementById("result").innerHTML = event.data;
};
} else {
document.getElementById("result").innerHTML =
"Sorry, Web Workers are not supported";
}
}
function stopWorker() {
if (typeof(worker) !== "undefined") {
worker.terminate();
worker = undefined;
}
}
</script>
Worker Script (worker.js)
var i = 0;
// Worker code runs in separate thread
function timedCount() {
i = i + 1;
// Send message to main script
postMessage(i);
setTimeout(timedCount, 500);
}
timedCount();
Worker Communication
Workers and the main script communicate through message passing:
Two-Way Communication
// Main script
var worker = new Worker("worker.js");
// Send data to worker
worker.postMessage({ cmd: 'calculate', data: [1, 2, 3, 4, 5] });
// Receive data from worker
worker.onmessage = function(e) {
console.log("Result from worker:", e.data);
};
// Handle errors
worker.onerror = function(e) {
console.error("Worker error:", e.message);
};
// ===== worker.js =====
onmessage = function(e) {
var data = e.data;
if (data.cmd === 'calculate') {
var sum = data.data.reduce((a, b) => a + b, 0);
// Send result back
postMessage(sum);
}
};
Web Worker Limitations
Workers run in a restricted environment with limited access to browser APIs:
❌ Not Available in Workers
-
DOM manipulation (no
window
,document
,parent
) - Direct access to the page (can't modify HTML)
- Most browser APIs (alert, confirm, prompt)
✅ Available in Workers
navigator
objectlocation
object (read-only)XMLHttpRequest
/fetch()
setTimeout()
,setInterval()
importScripts()
to load other scripts- All JavaScript features (arrays, objects, functions, etc.)
Practical Examples
Heavy Computation Example
<button onclick="calculatePrimes()">Calculate Primes</button>
<p id="status"></p>
<p id="primes"></p>
<script>
function calculatePrimes() {
document.getElementById("status").innerHTML = "Calculating...";
var worker = new Worker("prime-worker.js");
worker.postMessage({ max: 100000 });
worker.onmessage = function(e) {
document.getElementById("status").innerHTML = "Done!";
document.getElementById("primes").innerHTML =
"Found " + e.data.length + " prime numbers";
worker.terminate();
};
}
</script>
<!-- prime-worker.js -->
<script>
onmessage = function(e) {
var max = e.data.max;
var primes = [];
// Calculate prime numbers (CPU intensive)
for (var i = 2; i <= max; i++) {
var isPrime = true;
for (var j = 2; j < i; j++) {
if (i % j === 0) {
isPrime = false;
break;
}
}
if (isPrime) primes.push(i);
}
// Send result back
postMessage(primes);
};
</script>
Data Processing Example
// Main script
var worker = new Worker("data-processor.js");
// Send large dataset
worker.postMessage({
action: 'process',
data: largeDataArray
});
worker.onmessage = function(e) {
console.log("Processed data:", e.data.result);
console.log("Processing time:", e.data.time + "ms");
};
// ===== data-processor.js =====
onmessage = function(e) {
var startTime = Date.now();
if (e.data.action === 'process') {
var data = e.data.data;
// Process data (filter, map, reduce, etc.)
var result = data
.filter(item => item.value > 100)
.map(item => ({
...item,
doubled: item.value * 2
}))
.reduce((sum, item) => sum + item.doubled, 0);
var endTime = Date.now();
postMessage({
result: result,
time: endTime - startTime
});
}
};
Image Processing Example
// Apply grayscale filter using worker
function applyGrayscale(imageData) {
var worker = new Worker("image-worker.js");
worker.postMessage({ imageData: imageData });
worker.onmessage = function(e) {
var processedData = e.data;
// Draw processed image back to canvas
ctx.putImageData(processedData, 0, 0);
worker.terminate();
};
}
// ===== image-worker.js =====
onmessage = function(e) {
var imageData = e.data.imageData;
var data = imageData.data;
// Convert to grayscale
for (var i = 0; i < data.length; i += 4) {
var 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);
};
Importing Scripts in Workers
Workers can load external scripts using
importScripts()
:
Loading External Libraries
// Inside worker.js
// Import one or more scripts
importScripts('utils.js');
importScripts('library1.js', 'library2.js');
// Now you can use functions from imported scripts
onmessage = function(e) {
var result = utilityFunction(e.data); // From utils.js
postMessage(result);
};
Web Workers Best Practices
Best Practices
- Use for CPU-Intensive Tasks: Complex calculations, data processing, image manipulation
- Minimize Message Passing: Large data transfers can be slow
-
Terminate When Done: Call
terminate()
to free resources -
Handle Errors: Always implement
onerror
handler - Transfer Objects: Use transferable objects for large data (ArrayBuffers)
- Pool Workers: Reuse workers instead of creating many
- Avoid DOM Access: Workers can't access the DOM
- Check Support: Always verify Web Workers are available
- Debug Carefully: Use browser dev tools to debug workers
- Keep Workers Simple: One task per worker for clarity
Transferable Objects (Fast Data Transfer)
// Create large ArrayBuffer
var buffer = new ArrayBuffer(1024 * 1024 * 32); // 32 MB
// Transfer ownership (fast, no copying)
worker.postMessage({ buffer: buffer }, [buffer]);
// After transfer, buffer is no longer accessible in main thread
console.log(buffer.byteLength); // 0
// In worker
onmessage = function(e) {
var buffer = e.data.buffer;
console.log(buffer.byteLength); // 33554432
// Process buffer...
};