Skip to content

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.