Web Workers let you run JavaScript in background threads separate from the main (UI) thread. Use them to perform CPU-heavy or long-running tasks (data processing, image manipulation, heavy calculations) without freezing the page or janky UI.
Not for: DOM manipulation (workers have no access to DOM) — only message passing.
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Web Worker Example</title>
</head>
<body>
<h2>Web Worker Example</h2>
<button id="start">Start Heavy Task</button>
<button id="stop">Stop Worker</button>
<pre id="log"></pre>
<script>
let worker = null;
const log = document.getElementById('log');
document.getElementById('start').addEventListener('click', () => {
if (worker) {
log.textContent += '\\nWorker already running';
return;
}
// Create worker (assumes worker.js in same folder)
worker = new Worker('worker.js');
// Receive messages from worker
worker.onmessage = (e) => {
log.textContent += '\\n' + JSON.stringify(e.data);
};
worker.onerror = (err) => {
log.textContent += '\\nWorker error: ' + err.message;
worker.terminate();
worker = null;
};
// Send initial message to worker
worker.postMessage({ cmd: 'start', countTo: 100000000 });
log.textContent += '\\nWorker started';
});
document.getElementById('stop').addEventListener('click', () => {
if (worker) {
worker.terminate();
worker = null;
log.textContent += '\\nWorker terminated by main';
} else {
log.textContent += '\\nNo worker to stop';
}
});
</script>
</body>
</html>
// worker.js (runs in background thread)
self.onmessage = function(e) {
const data = e.data;
if (data.cmd === 'start') {
const n = data.countTo || 1000000;
// Example: simple heavy loop with intermittent progress posts
let sum = 0;
const chunk = Math.floor(n / 10) || 1;
for (let i = 1; i <= n; i++) {
sum += i; // compute sum to simulate work
if (i % chunk === 0) {
// send progress back to main thread
self.postMessage({ progress: i / n, partialSum: sum });
}
}
// final message
self.postMessage({ done: true, result: sum });
}
};
// optional: handle errors inside worker
self.onerror = function(err) {
self.postMessage({ error: err.message });
};
Save index.html and worker.js in the same folder and open index.html (served by a local server ideally — some browsers restrict workers via file://).
Use ArrayBuffer transfer to move ownership without clone:
// main thread
const ab = new ArrayBuffer(1024 * 1024); // 1MB
worker.postMessage(ab, [ab]); // transfer ownership
// after this, `ab.byteLength` may be 0 in main
// worker.js receives the ArrayBuffer and uses it
self.onmessage = e => {
const buffer = e.data;
// process buffer...
};
Shared workers allow multiple windows/tabs to talk to the same background script. Use new SharedWorker('shared.js') and port.postMessage() / port.onmessage. Browser support is more limited than dedicated workers.
Most modern browsers support Web Workers (Dedicated Workers) broadly. Shared Workers and some advanced features have less universal support — test on target browsers.
// worker.js
importScripts('biglib.js'); // synchronous import
// now biglib functions are available in worker scope
Create N workers and queue tasks; assign tasks to the next available worker. This avoids creating/terminating workers repeatedly and reduces overhead.
Be cautious when executing untrusted code inside workers. Workers have access to network and storage APIs; ensure you only run code you control or sanitize inputs.