JavaScript normally runs on a single main thread alongside page rendering and click handling. When heavy loops, image processing, or large JSON parsing run on the main thread, buttons freeze and scrolling stutters. An HTML Web Worker moves that heavy work onto a separate background thread.
Because Web Workers run in an isolated thread, they cannot access document, window, or DOM elements directly. Instead, your page and the worker pass copies of data back and forth asynchronously using postMessage() and onmessage listeners.
Once a task completes, call worker.terminate() from the main script (or self.close() inside the worker) to free background memory cleanly.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HTML Web Worker Demo</title>
</head>
<body style="font-family: system-ui, sans-serif; padding: 20px;">
<button id="calc-btn" type="button">Run Background Sum</button>
<p id="worker-result">UI stays clickable while worker runs.</p>
<script>
const resultEl = document.getElementById('worker-result');
document.getElementById('calc-btn').addEventListener('click', () => {
if (!window.Worker) {
resultEl.textContent = 'Web Workers are not supported in this browser.';
return;
}
const blob = new Blob([`
self.onmessage = function(e) {
let sum = 0;
for (let i = 1; i <= e.data; i++) sum += i;
self.postMessage(sum);
};
`], { type: 'application/javascript' });
const worker = new Worker(URL.createObjectURL(blob));
worker.onmessage = (e) => {
resultEl.textContent = 'Computed total: ' + e.data;
worker.terminate();
};
worker.postMessage(5000000);
});
</script>
</body>
</html>
In production apps, place worker code in a dedicated file (new Worker('/js/csv-worker.js')). Ideal workloads include parsing huge CSV imports, compressing images, running spellcheckers, and computing cryptographic hashes.
document.getElementById() inside a worker: Workers have no DOM access; send computed results back with postMessage() so the main thread updates the UI.file:// URL: Browsers block new Worker('worker.js') over file:// due to origin security rules. Test through a local dev server.worker.terminate() when one-off jobs finish.Yes. Workers have full access to fetch(), WebSocket, setTimeout(), and IndexedDB even though they cannot touch the DOM.
Normal objects are cloned, which takes time if massive. For huge binary arrays, pass a transferable ArrayBuffer as the second argument to move ownership instantly.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With
Experiment with the code from this lesson in our interactive playground.