Why move work off the main thread

The browser runs all page JavaScript, rendering, and garbage collection on a single main thread. When too much JavaScript executes there, the browser can't respond promptly to user input, which directly hurts the user experience. Mobile app development solved this by pushing work to additional threads; on the web, the equivalent tool is the worker: an entire JavaScript scope running on a separate thread without shared memory.

Workers let you run script in the background so the main thread stays free for UI work. There are two distinct types—web workers and service workers—and each suits different jobs.

Web workers vs. service workers

Shared characteristics

Both web workers and service workers execute on secondary threads, so they don't block the user interface. They also share the same limitations: no access to the Window or Document objects, no direct DOM interaction, and only a restricted set of browser APIs.

Key differences

The behaviors diverge in three important ways:

  • Network and background events: Service workers can intercept network requests through the fetch event and listen for Push API events via the push event. Web workers have no such capabilities.
  • Scope: A page can spawn many web workers, but only one service worker controls all active tabs within its registration scope.
  • Lifespan: A web worker's life is bound to the tab it belongs to—closing the tab terminates the worker. Service workers have an independent lifecycle and can keep running even with no open tabs for the site.

Which worker for which job

Those differences map naturally onto use cases.

Web workers are for offloading heavy computation from the main thread to avoid blocking the UI. When the team behind the PROXX game built it, they moved game logic and state maintenance into web workers to keep the main thread available for user input and animation.

Service workers act as a network proxy: caching, offline support, background tasks, and request interception are their domain. A podcast PWA, for example, can use a service worker with the Background Fetch API to let users download episodes for offline listening. Because the service worker is independent of any tab, the download continues even if the user closes the tab.

Libraries that simplify worker communication

Writing window-to-worker communication from scratch means dealing with low-level APIs. Two libraries abstract that process for each worker type.

Comlink is a small (1.6 kB) RPC library that handles the underlying messaging details when using web workers. Sites like PROXX and Squoosh use it. It lets you treat the worker's functions as if they were local, hiding the postMessage boilerplate.

Workbox for service workers

Workbox packages established best practices for service workers—caching, offline, background sync—behind a friendly interface. Its workbox-window module provides a convenient channel for exchanging messages between the service worker and the page.