HTML Web Workers
JavaScript normally runs in a single thread — it does one thing at a time. When JavaScript runs a long, heavy task, it blocks everything else: buttons stop responding, animations freeze, and the page appears stuck. Web Workers solve this by running JavaScript code in a background thread, separate from the main page thread.
The Problem Web Workers Solve
Visual Diagram — Without Web Workers
Main Thread (only one thread):
┌──────┬──────────────────────────┬──────┐
│ Page │ Heavy task running... │ Page │
│ runs │ (sorting 1M records) │ runs │
└──────┴──────────────────────────┴──────┘
↑
Page is FROZEN here — no clicks, no animations
With Web Workers (two threads):
Main Thread: ┌──────┬──────────────────────────┬──────┐
│ Page │ Page works fine! │ Page │
└──────┴──────────────────────────┴──────┘
Worker Thread: ┌──────────────────────────┐
│ Heavy task running... │
└──────────────────────────┘
Page stays responsive while the background work continues.
What Web Workers Can and Cannot Do
Web Workers CAN: ✓ Run JavaScript code ✓ Use fetch() to load data from URLs ✓ Use timers (setTimeout, setInterval) ✓ Use Web Storage (localStorage, sessionStorage) ✓ Do calculations, sort data, process large files ✓ Communicate with the main page via messages Web Workers CANNOT: ✗ Access the DOM (no document.getElementById etc.) ✗ Access the window object ✗ Access the parent page's variables directly ✗ Display alerts or prompts
Workers live in isolation. They communicate with the main page only through messages — like passing notes through a door.
Creating a Web Worker
A Web Worker lives in a separate JavaScript file. The main page creates the worker by pointing to that file.
Step 1 — Create the Worker File (worker.js)
// worker.js
// This code runs in the background thread
self.addEventListener("message", function(event) {
const input = event.data; // receive data from main page
// Do the heavy work here
let result = 0;
for (let i = 0; i < input; i++) {
result += i;
}
self.postMessage(result); // send result back to main page
});
Step 2 — Use the Worker in Your HTML Page
<button onclick="startWork()">Calculate Sum</button>
<p id="output">Result will appear here...</p>
<script>
function startWork() {
// Check browser support
if (typeof Worker === "undefined") {
document.getElementById("output").textContent = "Web Workers not supported";
return;
}
// Create the worker
const worker = new Worker("worker.js");
// Send data to the worker
worker.postMessage(10000000); // ask it to sum numbers 0 to 9,999,999
// Listen for the result
worker.onmessage = function(event) {
document.getElementById("output").textContent = "Sum: " + event.data;
worker.terminate(); // stop the worker when done
};
// Handle errors
worker.onerror = function(error) {
console.log("Worker error:", error.message);
};
}
</script>
The Messaging System
Visual Diagram — Two-Way Communication
Main Page Worker (worker.js)
──────────────────────────────────────────────────────
worker.postMessage(data) ──────→ event.data
self.postMessage(result)
worker.onmessage = fn ←────── (sends result back)
Both sides use postMessage() to send.
Both sides use onmessage to receive.
Data travels as a copy — not a shared reference.
Sending Complex Data
You can send any data type that can be serialized: strings, numbers, booleans, arrays, objects. The worker receives a copy of the data — changes in the worker do not affect the original.
// Main page sends an object
worker.postMessage({
task: "sort",
data: [5, 2, 8, 1, 9, 3],
order: "ascending"
});
// worker.js receives it
self.addEventListener("message", function(event) {
const { task, data, order } = event.data;
if (task === "sort") {
const sorted = order === "ascending"
? data.sort((a, b) => a - b)
: data.sort((a, b) => b - a);
self.postMessage({ result: sorted });
}
});
Transferable Objects — Faster Data Sharing
Normally, data sent between the page and a worker is copied. For large data like ArrayBuffer, this copying is slow. You can transfer ownership instead of copying — the original loses access to the data, but the transfer is instant.
// Main page — transfer a large buffer to the worker const buffer = new ArrayBuffer(1024 * 1024); // 1 MB worker.postMessage(buffer, [buffer]); // second argument lists transferables // buffer is now empty/detached in main page
Dedicated vs Shared Workers
Dedicated Worker (most common)
A dedicated worker belongs to one page. Only that page can communicate with it. This is the type covered in the examples above — created with new Worker("file.js").
Shared Worker
A shared worker can be connected to by multiple browser tabs or windows from the same origin. It continues running as long as any connected tab remains open.
// Create a shared worker
const sharedWorker = new SharedWorker("shared-worker.js");
// Communicate through the port
sharedWorker.port.postMessage("Hello");
sharedWorker.port.onmessage = function(event) {
console.log("Received:", event.data);
};
sharedWorker.port.start();
Terminating a Worker
Workers keep running until explicitly terminated. Always terminate a worker when its task is complete to free up memory and CPU.
// From the main page: worker.terminate(); // From inside the worker itself: self.close();
Practical Use Cases
Task Why Use a Worker ---------------------------- ------------------------------------------ Sorting 100,000 records Would freeze page for several seconds Image processing/filters Heavy pixel calculations Real-time data parsing Parsing large JSON responses Compression/encryption CPU-intensive operations Background data polling Fetch data every 30s without blocking UI Game physics calculations Keep game loop smooth Scientific simulations Number-crunching without lag
Error Handling in Workers
// Catch errors in the worker from the main page:
worker.onerror = function(error) {
console.log("Error in worker:");
console.log(" Message:", error.message);
console.log(" File:", error.filename);
console.log(" Line:", error.lineno);
error.preventDefault(); // prevent error from propagating further
};
// Catch unhandled errors inside the worker:
// worker.js
self.addEventListener("error", function(e) {
console.log("Worker internal error:", e.message);
});
Browser Support
Web Workers are supported in all modern browsers including Chrome, Firefox, Safari, and Edge. They also work in mobile browsers on Android and iOS. For very old browsers (IE10 and below), you need to check for support with typeof Worker !== "undefined" before using them.
Web Workers bring multi-threaded thinking to the browser. They are the correct tool whenever a JavaScript task takes more than 50–100 milliseconds — anything longer starts to make the page feel sluggish to users.
