Dedicated Workers
This lesson explains Dedicated Workers with clear examples, use cases, and best practices so you can use HTML5 APIs confidently in real web applications.
HTML5 Dedicated Workers API Overview
The HTML5 Dedicated Workers API allows JavaScript code to run in a
background thread separate from the main browser UI thread. This helps web
applications perform heavy calculations, file processing, data parsing,
image processing, and long-running tasks without freezing the user
interface.
A dedicated worker is connected to the script that created it. It
communicates with the main thread using message passing through
postMessage() and the message event. Dedicated
workers cannot directly access the DOM, but they can process data,
perform network requests, and send results back to the main page.
| Feature | Description |
| API Name | Dedicated Workers API |
| Main Object | Worker |
| Thread Type | Background JavaScript thread |
| Main Method | postMessage() |
| Main Event | message |
| Best Use | CPU-heavy work without blocking the UI |
Dedicated Worker Basic Example
// main.js
if ("Worker" in window) {
const worker = new Worker("/workers/calculator-worker.js");
worker.postMessage(
{
type: "CALCULATE",
numbers: [10, 20, 30, 40]
}
);
worker.addEventListener("message", (event) => {
console.log("Worker result:", event.data);
});
worker.addEventListener("error", (error) => {
console.error("Worker error:", error.message);
});
} else {
console.log("Web Workers are not supported.");
}
// calculator-worker.js
self.addEventListener("message", (event) => {
if (event.data.type === "CALCULATE") {
const total = event.data.numbers.reduce(
(sum, value) => sum + value,
0
);
self.postMessage(
{
type: "RESULT",
total
}
);
}
});
The main page creates a worker, sends data to it, and listens for the
result. The worker performs the calculation in the background and sends
the result back using self.postMessage().
How Dedicated Workers Work
Dedicated workers use a simple message-based communication model. The main
script and worker script do not share direct access to variables or DOM
elements.
| Step | Description | Example Syntax |
| Create Worker | Creates a new background thread. | new Worker("worker.js") |
| Send Data | Sends data from main thread to worker. | worker.postMessage(data) |
| Receive Data | Listens for worker responses. | worker.addEventListener("message", callback) |
| Worker Processing | Runs calculations or processing in the worker. | self.addEventListener("message", callback) |
| Return Result | Sends processed result back to main thread. | self.postMessage(result) |
| Terminate Worker | Stops the worker when finished. | worker.terminate() |
What Dedicated Workers Can and Cannot Access
| Allowed in Worker | Not Allowed in Worker |
| JavaScript calculations | Direct DOM access |
fetch() requests | document |
Timers such as setTimeout() | window DOM APIs |
| Data parsing and transformation | Direct HTML element updates |
| File and Blob processing | Direct event listeners on page elements |
| Importing scripts or modules | Accessing page CSS styles directly |
Dedicated Worker Methods and Events
| Method / Event | Where Used | Purpose |
new Worker() | Main Thread | Creates a dedicated worker. |
postMessage() | Main Thread / Worker | Sends data between threads. |
message | Main Thread / Worker | Receives messages from the other side. |
error | Main Thread | Handles worker script errors. |
messageerror | Main Thread / Worker | Handles message serialization errors. |
terminate() | Main Thread | Stops the worker immediately. |
self.close() | Worker | Allows the worker to stop itself. |
Heavy Calculation Example
// main.js
const worker = new Worker("/workers/prime-worker.js");
worker.postMessage(
{
type: "FIND_PRIMES",
limit: 100000
}
);
worker.addEventListener("message", (event) => {
console.log("Prime count:", event.data.count);
console.log("Last prime:", event.data.lastPrime);
});
// prime-worker.js
function isPrime(number) {
if (number < 2) {
return false;
}
for (let i = 2; i <= Math.sqrt(number); i += 1) {
if (number % i === 0) {
return false;
}
}
return true;
}
self.addEventListener("message", (event) => {
if (event.data.type === "FIND_PRIMES") {
const primes = [];
for (let i = 2; i <= event.data.limit; i += 1) {
if (isPrime(i)) {
primes.push(i);
}
}
self.postMessage(
{
type: "PRIMES_COMPLETE",
count: primes.length,
lastPrime: primes[primes.length - 1]
}
);
}
});
Prime number calculation can be CPU-heavy. Running it inside a dedicated
worker prevents the main page from freezing while the browser continues
responding to user interactions.
Fetch Data Inside a Worker
// data-worker.js
self.addEventListener("message", async (event) => {
if (event.data.type === "LOAD_USERS") {
try {
const response = await fetch("/api/users");
const users = await response.json();
const activeUsers = users.filter(
(user) => user.active
);
self.postMessage(
{
type: "USERS_LOADED",
users: activeUsers
}
);
} catch (error) {
self.postMessage(
{
type: "ERROR",
message: error.message
}
);
}
}
});
Workers can use fetch(), making them useful for loading,
filtering, sorting, and transforming large API responses before sending
smaller results back to the main thread.
Module Worker Example
// main.js
const worker = new Worker(
"/workers/module-worker.js",
{ type: "module" }
);
worker.postMessage(
{
type: "FORMAT_PRICE",
amount: 1299.99
}
);
// module-worker.js
import { formatCurrency } from "./format.js";
self.addEventListener("message", (event) => {
if (event.data.type === "FORMAT_PRICE") {
self.postMessage(
{
type: "PRICE_FORMATTED",
value: formatCurrency(event.data.amount)
}
);
}
});
Module workers support JavaScript modules, allowing developers to use
import and export syntax inside worker files.
This makes worker code easier to organize in modern applications.
Dedicated Worker Use Cases
| Use Case | How Dedicated Workers Help |
| Large Data Processing | Filter, sort, transform, and aggregate large datasets. |
| Image Processing | Resize, compress, analyze, or transform image data. |
| File Parsing | Parse CSV, JSON, XML, logs, or large text files. |
| Math Calculations | Run statistics, simulations, encryption, and heavy calculations. |
| Search Indexing | Build client-side search indexes without blocking UI. |
| Background Fetch Processing | Load and transform API responses before UI rendering. |
| Compression | Compress or decompress files in the background. |
| Real-Time Apps | Process frequent data updates efficiently. |
Dedicated Worker Best Practices
- Use dedicated workers for CPU-heavy work, not simple UI updates.
- Keep message payloads small when possible.
- Use transferable objects for large binary data when appropriate.
- Terminate workers when they are no longer needed.
- Handle
error and messageerror events. - Use module workers for cleaner and maintainable worker code.
- Do not try to access the DOM directly from a worker.
- Provide fallback behavior when workers are not supported.
Common Dedicated Worker Mistakes
- Trying to access
document or DOM elements inside a worker. - Sending very large objects repeatedly between threads.
- Forgetting to terminate workers after completing the task.
- Not handling worker errors.
- Using workers for small tasks that do not need background processing.
- Expecting workers to share variables directly with the main thread.
- Blocking the worker with infinite loops.
- Not checking browser support before creating a worker.
Key Takeaways
- Dedicated workers run JavaScript in a background thread.
- They prevent heavy tasks from blocking the main UI thread.
- Use
new Worker() to create a worker. - Use
postMessage() and message events for communication. - Workers cannot directly access the DOM.
- Dedicated workers are ideal for calculations, parsing, file processing, and data transformation.
Pro Tip
Use dedicated workers when a task can run independently from the UI.
Heavy loops, data parsing, image processing, and search indexing are good
candidates. DOM updates, button clicks, and small calculations should stay
on the main thread.