Workers overview

How web workers and service workers can improve the performance of your site, and when to use a web worker versus a service worker.

Andrew Guan
Andrew Guan
Demián Renzulli
Demián Renzulli

This overview explains how web workers and service workers can improve the performance of your website, and when to use a web worker versus a service worker. Check out the rest of this series for specific patterns of window and service worker communication.

The browser uses a single thread (the main thread) to run all the JavaScript in a web page, as well as to perform tasks like rendering the page and performing garbage collection. Running excessive JavaScript code can block the main thread, delaying the browser from performing these tasks and leading to a poor user experience.

In iOS/Android application development, a common pattern to ensure that the app's main thread remains free to respond to user events is to offload operations to additional threads. In fact, in the latest versions of Android, blocking the main thread for too long leads to an app crash.

On the web, JavaScript was designed around the concept of a single thread, and lacks capabilities needed to implement a multithreading model like the one apps have, like shared memory.

Despite these limitations, a similar pattern can be achieved in the web by using workers to run scripts in background threads, allowing them to perform tasks without interfering with the main thread. Workers are an entire JavaScript scope running on a separate thread, without any shared memory.

In this post you'll learn about two different types of workers (web workers and service workers), their similarities and differences, and the most common patterns for using them in production websites.

Diagram showing two links between the Window object and a web worker and service worker.

Web workers and service workers

Similarities

Web workers and service workers are two types of workers available to websites. They have some things in common:

  • Both run in a secondary thread, allowing JavaScript code to execute without blocking the main thread and the user interface.
  • They don't have access to the Window and Document objects, so they can't interact with the DOM directly, and they have limited access to browser APIs.

Differences

One might think that most things that can be delegated to a web worker can be done in a service worker and vice versa, but there are important differences between them:

  • Unlike web workers, service workers allow you to intercept network requests (via the fetch event) and to listen for Push API events in the background (via the push event).
  • A page can spawn multiple web workers, but a single service worker controls all the active tabs under the scope it was registered with.
  • The lifespan of the web worker is tightly coupled to the tab it belongs to, while the service worker's lifecycle is independent of it. For that reason, closing the tab where a web worker is running will terminate it, while a service worker can continue running in the background, even when the site doesn't have any active tabs open.

Use cases

The differences between both types of workers suggest in which situations one might want to use one or the other:

Use cases for web workers are more commonly related to offloading work (like heavy computations) to a secondary thread, to avoid blocking the UI.

Diagram showing a link from the Window object to a web worker.
  • Example: the team that built the videogame PROXX wanted to leave the main thread as free as possible to take care of user input and animations. To achieve that, they used web workers to run the game logic and state maintenance on a separate thread.
A screenshot of the videogame PROXX.

Service workers tasks are generally more related to acting as a network proxy, handling background tasks, and things like caching and offline.

A screenshot of the videogame PROXX.

Example: In a podcast PWA, one might want to allow users to download complete episodes to listen to them while offline. A service worker, and, in particular, the Background Fetch API can be used to that end. That way, if the user closes the tab while the episode is downloading, the task doesn't have to be interrupted.

A screenshot of a Podcast PWA.
The UI is updated to indicate the progress of a download (left). Thanks to service workers, the operation can continue running when all tabs have been closed (right).

Tools and libraries

Window and worker communication can be implemented by using different lower level APIs. Fortunately, there are libraries that abstract this process, taking care of the most common use cases. In this section, we'll cover two of them that take care of window to web workers and service workers respectively: Comlink and Workbox.

A screenshot of the videogame PROXX.

Comlink is a small (1.6k) RPC library that takes care of many underlying details when building websites that use Web Workers. It has been used in websites like PROXX and Squoosh. A summary of its motivations and code samples can be found here.

Workbox

Workbox is a popular library to build websites that use service workers. It packages a set of best practices around things like caching, offline, background synchronization, etc. The workbox-window module provides a convenient way to exchange messages between the service worker and the page.

Next steps

The rest of this series focuses on patterns for window and service worker communication:

  • Imperative caching guide: Calling a service worker from the page to cache resources in advance (e.g. in prefetching scenarios).
  • Broadcast updates: Calling the page from the service worker to inform about important updates (e.g. a new version of the website is available).
  • Two-way communication: Delegating a task to a service worker (e.g. a heavy download), and keeping the page informed on the progress.

For patterns of window and web worker communication check out: Use web workers to run JavaScript off the browser's main thread.