HTML APIs
Explain Web Worker in HTML
A Web Worker runs JavaScript on a background thread, so heavy computation happens off the main thread and the user interface stays smooth and responsive instead of freezing.
What is a Web Worker and why use one?
JavaScript in the browser normally runs on a single main thread that also handles rendering and user input. If a script does heavy work - parsing a large file, image processing, complex maths - the whole page freezes until it finishes. A Web Worker solves this by running a separate script on its own background thread, in parallel with the main thread.
Because the worker runs elsewhere, long tasks no longer block scrolling, clicks or animations. Use one for CPU-intensive jobs: crunching data, encrypting, compressing, or parsing big JSON. The main thread and the worker talk by passing messages back and forth.
Syntax
You create a dedicated worker with new Worker(url). Normally url points to a separate .js file, but you can also build the worker from an inline string using a Blob and URL.createObjectURL - that is how the runnable demo below works without a second file. The two threads communicate with postMessage() to send and the onmessage handler to receive.
// main.js
const worker = new Worker("worker.js");
worker.postMessage(42); // send data to the worker
worker.onmessage = (e) => console.log("Result:", e.data);
// worker.js (background thread)
self.onmessage = (e) => {
const result = e.data * 2;
self.postMessage(result); // send the answer back
};Example: a heavy calculation that keeps the UI responsive
This complete page builds the worker from an inline Blob URL so it runs entirely in the editor with no second file. Click the button: the worker sums millions of square roots on a background thread while the page stays interactive.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Web Worker demo</title>
<style>
body { font-family: system-ui, sans-serif; padding: 24px; }
button { padding: 8px 16px; font-size: 15px; cursor: pointer; }
#result { margin-top: 16px; font-size: 18px; }
</style>
</head>
<body>
<h3>Background computation</h3>
<button id="run">Compute in a worker</button>
<div id="result">Idle</div>
<script>
// The worker's code as a string
const workerCode = `
self.onmessage = (e) => {
const n = e.data;
let sum = 0;
for (let i = 0; i < n; i++) sum += Math.sqrt(i);
self.postMessage(sum);
};
`;
// Turn the string into a Blob URL so new Worker() can load it inline
const blob = new Blob([workerCode], { type: "application/javascript" });
const worker = new Worker(URL.createObjectURL(blob));
const result = document.getElementById("result");
document.getElementById("run").addEventListener("click", () => {
result.textContent = "Working on the background thread...";
worker.postMessage(1e8); // send a big number to crunch
});
worker.onmessage = (e) => {
result.textContent = "Result: " + e.data.toFixed(2);
};
worker.onerror = (err) => {
result.textContent = "Worker error: " + err.message;
};
</script>
</body>
</html>How communication works
Data passed through postMessage() is copied using the structured clone algorithm, not shared by reference - the two threads never share mutable state directly. You can pass numbers, strings, arrays, objects, ArrayBuffers and more. Large buffers can be transferred (moved rather than copied) for performance using the transfer list argument.
Limitations: no DOM access
A worker runs in a stripped-down global scope (WorkerGlobalScope), not the window. Here is what it can and cannot do:
- No access to the DOM - it cannot touch document or read/change the page's HTML elements.
- No access to window, parent or the page's global variables.
- It CAN use fetch, XMLHttpRequest, WebSockets, setTimeout/setInterval, IndexedDB and importScripts().
- To change the UI, send a message back to the main thread and let that thread update the DOM.
- A file-based worker must be served from the same origin (subject to CORS).
Worker methods and events
| Member | Side | Purpose |
|---|---|---|
| new Worker(url) | Main | Create a worker from a file or Blob URL |
| worker.postMessage(data) | Main | Send data to the worker |
| worker.onmessage | Main | Receive results from the worker |
| worker.terminate() | Main | Kill the worker immediately |
| self.onmessage | Worker | Receive data from the main thread |
| self.postMessage(data) | Worker | Send results back to the main thread |
| self.close() | Worker | Shut the worker down from inside |
Web Workers are supported in all modern browsers. Beyond the dedicated worker shown here, there are Shared Workers (shared by multiple tabs of one origin) and Service Workers (a specialised worker for caching, offline support and push, sitting between the page and the network).
Always call worker.terminate() (or self.close() inside the worker) when the task is done. Idle workers keep their thread and memory alive. Reuse one long-lived worker for repeated tasks rather than spawning a new one each time, and remember to revoke the Blob URL when you are finished with it.
Key Takeaways
- Web Workers run JavaScript on a background thread so the UI never freezes.
- Create one with new Worker(url) - from a file, or from a Blob URL to run inline.
- The two threads communicate only by copying data through postMessage / onmessage.
- Workers cannot touch the DOM, document or window - send results back to update the UI.
- Terminate idle workers to free their thread and memory.
