2HAASStart a project
All posts

Code Snippets

Web Workers: move heavy work off the main thread

Heavy JavaScript freezes the page because it shares a thread with the interface. A Web Worker runs that work in the background. A working example and the limits to know.

The 2HAAS team2 min read

JavaScript in the browser runs on the same thread that draws the page and handles clicks. Apply a filter to a large image there and the interface freezes until it finishes: no scrolling, no button feedback, nothing.

A Web Worker is a second thread. It runs a script in the background and talks to the page through messages, so the interface stays free.

A working example

The page creates the worker, sends it the pixels and waits for the result.

// main.js
const worker = new Worker(new URL("./filter.worker.js", import.meta.url), { type: "module" });

function applyFilter(imageData) {
  const { width, height, data } = imageData;
  // The second argument transfers the buffer instead of copying it
  worker.postMessage({ pixels: data.buffer, width, height }, [data.buffer]);
}

worker.addEventListener("message", (event) => {
  drawResult(new Uint8ClampedArray(event.data));
});

worker.addEventListener("error", (event) => {
  showError(event.message);
});

The worker does the slow part.

// filter.worker.js
self.addEventListener("message", (event) => {
  const { pixels, width, height } = event.data;
  const result = grayscale(new Uint8ClampedArray(pixels), width, height);
  self.postMessage(result.buffer, [result.buffer]);
});

function grayscale(data) {
  for (let i = 0; i < data.length; i += 4) {
    const value = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
    data[i] = data[i + 1] = data[i + 2] = value;
  }
  return data;
}

While the worker runs, the user can keep scrolling and clicking.

What a worker cannot do

  • No DOM access. A worker cannot read or change the page. It computes and returns a result.
  • No shared variables. Everything passes through postMessage.
  • Messages are copied. Large objects take time to copy. Transfer an ArrayBuffer, as above, and ownership moves without a copy.

Good candidates

  • image and video processing,
  • parsing large JSON or CSV files,
  • building a search index,
  • hashing and encryption,
  • heavy calculations for charts or simulations.

A quick task is a poor candidate. Starting a worker and passing messages has a cost of its own, so save workers for work that would block the page for a noticeable time.

Practical tips

  1. Reuse workers. Create one and send it many jobs. Creating a worker for each task wastes startup time.
  2. Stop what you no longer need. worker.terminate() ends a worker when the user leaves the screen.
  3. Handle errors. Listen for the error event, or a failure in the background goes unseen.
  4. Consider a helper library. Comlink wraps the message passing so that calling a worker looks like calling an async function.

Workers are one way to keep a page responsive. For the others, see keeping the main thread free.

Keep reading

  1. Code Snippets2 min read

    Intersection Observer: react when elements scroll into view

    Scroll listeners that measure element positions are slow and fiddly. Intersection Observer tells you when an element enters the viewport, with a few lines of code.

  2. Code Snippets4 min read

    Simplify Popups with HTML Popover API

    Learn how to effortlessly create accessible popups, tooltips, and modals using the native HTML Popover API, reducing custom JavaScript and improving user experience.

  3. Guides2 min read

    How to reduce JavaScript bundle size

    Every kilobyte of JavaScript is downloaded, parsed and run before a page responds. Measure the bundle, split it by route, lazy load the rest and trim dependencies.

Tell us what’s in the way.

A few lines is enough. You get a candid yes, a candid no, or a sharper question.

  • Working software in week four
  • A reply in one business day
  • Handover is the deliverable

Prefer email? hello@2haas.com

The problem, who it is for, and what is in the way right now.

One business day to a real reply. No sales sequence, no call required.