Web Worker API
Run JavaScript in background threads without blocking the UI
โก What are Web Workers?
Web Workers allow you to run JavaScript code in background threads, separate from the main UI thread. This prevents heavy computations from freezing the user interface and keeps your web app responsive.
// Create a new Web Worker
const worker = new Worker('worker.js');
// Send data to worker
worker.postMessage({command: 'start', data: [1, 2, 3, 4, 5]});
// Receive data from worker
worker.onmessage = function(e) {
console.log('Result from worker:', e.data);
};
Output:
Result from worker: {result: 15, message: "Sum calculated"}
UI remains responsive during calculation!
Web Worker Features
Background Thread
Runs separately from main UI
new Worker('script.js')
Message Passing
Communicate via messages
worker.postMessage(data)
No DOM Access
Cannot directly modify HTML
// Not allowed in worker
Non-blocking
UI stays responsive
// Heavy tasks don't freeze UI
๐น Creating Your First Worker
Let's create a simple Web Worker for calculations:
๐ worker.js (Worker Script)
// This code runs in the worker thread
self.onmessage = function(e) {
const { command, numbers } = e.data;
if (command === 'sum') {
// Simulate heavy calculation
let result = 0;
for (let i = 0; i < numbers.length; i++) {
result += numbers[i];
// Simulate slow processing
for (let j = 0; j < 1000000; j++) {
// Busy work
}
}
// Send result back to main thread
self.postMessage({
command: 'sum_complete',
result: result,
message: 'Calculation finished!'
});
}
};
๐ main.js (Main Thread)
// Create worker
const worker = new Worker('worker.js');
// Handle messages from worker
worker.onmessage = function(e) {
const { command, result, message } = e.data;
if (command === 'sum_complete') {
document.getElementById('result').innerHTML = `
<p>Result: ${result}</p>
<p>${message}</p>
`;
}
};
// Handle worker errors
worker.onerror = function(error) {
console.error('Worker error:', error);
};
// Start calculation
function startCalculation() {
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
worker.postMessage({
command: 'sum',
numbers: numbers
});
document.getElementById('result').innerHTML = 'Calculating...';
}
Worker Demo:
Result: 55
Calculation finished!
UI remained responsive during calculation
๐น Inline Workers
Create workers without separate files using Blob URLs:
// Create worker code as a string
const workerCode = `
self.onmessage = function(e) {
const { numbers } = e.data;
// Calculate factorial
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
const results = numbers.map(num => ({
number: num,
factorial: factorial(num)
}));
self.postMessage({
type: 'factorial_results',
results: results
});
};
`;
// Create blob and worker
const blob = new Blob([workerCode], { type: 'application/javascript' });
const worker = new Worker(URL.createObjectURL(blob));
// Use the worker
worker.onmessage = function(e) {
if (e.data.type === 'factorial_results') {
console.log('Factorial results:', e.data.results);
displayResults(e.data.results);
}
};
function calculateFactorials() {
const numbers = [5, 6, 7, 8, 9];
worker.postMessage({ numbers: numbers });
}
function displayResults(results) {
const output = results.map(r =>
`${r.number}! = ${r.factorial}`
).join('<br>');
document.getElementById('factorial-results').innerHTML = output;
}
Inline Worker Results:
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
๐น Progress Updates
Send progress updates from worker to main thread:
// Worker code for progress updates
const progressWorkerCode = `
self.onmessage = function(e) {
const { task, iterations } = e.data;
if (task === 'process_data') {
for (let i = 0; i <= iterations; i++) {
// Simulate work
for (let j = 0; j < 1000000; j++) {
Math.random();
}
// Send progress update
const progress = Math.round((i / iterations) * 100);
self.postMessage({
type: 'progress',
progress: progress,
current: i,
total: iterations
});
// Small delay to see progress
if (i % 10 === 0) {
// Allow other operations
}
}
self.postMessage({
type: 'complete',
message: 'Processing finished!'
});
}
};
`;
// Create progress worker
const progressBlob = new Blob([progressWorkerCode], { type: 'application/javascript' });
const progressWorker = new Worker(URL.createObjectURL(progressBlob));
// Handle progress updates
progressWorker.onmessage = function(e) {
const { type, progress, current, total, message } = e.data;
if (type === 'progress') {
updateProgressBar(progress);
document.getElementById('progress-text').textContent =
`Processing: ${current}/${total} (${progress}%)`;
} else if (type === 'complete') {
document.getElementById('progress-text').textContent = message;
}
};
function updateProgressBar(progress) {
const progressBar = document.getElementById('progress-bar');
progressBar.style.width = progress + '%';
}
function startProgressTask() {
progressWorker.postMessage({
task: 'process_data',
iterations: 100
});
}
Progress Demo:
Processing: 75/100 (75%)
๐น Worker Limitations
Important things to know about Web Workers:
โ What Workers CAN'T Do:
- DOM Access: Cannot modify HTML elements directly
- Parent Object: No access to window, document, parent
- Local Files: Cannot access local file system
- Other Workers: Cannot create other workers directly
โ What Workers CAN Do:
- Calculations: Heavy mathematical operations
- Data Processing: Parse large datasets
- Network Requests: Fetch data from APIs
- Timers: setTimeout, setInterval
๐น Terminating Workers
Properly manage worker lifecycle:
// Create worker
const myWorker = new Worker('worker.js');
// Terminate worker from main thread
function stopWorker() {
myWorker.terminate();
console.log('Worker terminated');
}
// Terminate worker from inside worker (worker.js)
// self.close(); // Call this inside worker to terminate itself
// Example: Auto-terminate after task completion
const autoWorkerCode = `
self.onmessage = function(e) {
const { task } = e.data;
if (task === 'quick_calculation') {
// Do some work
const result = Math.random() * 1000;
// Send result
self.postMessage({
type: 'result',
value: result
});
// Auto-terminate
self.close();
}
};
`;
// Good practice: Clean up workers
window.addEventListener('beforeunload', function() {
if (myWorker) {
myWorker.terminate();
}
});
Worker Management:
โ Always terminate workers when done
โ Clean up on page unload
โ Workers can self-terminate with close()
โ Use terminate() from main thread