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.
How workers can improve your website
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.
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.
Web workers and service workers
- They don't have access to the
Documentobjects, so they can't interact with the DOM directly, and they have limited access to browser APIs.
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
fetchevent) and to listen for Push API events in the background (via the
- 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.
For relatively short bits of work like sending a message, the browser won't likely terminate a service worker when there are no active tabs, but if the task takes too long the browser will terminate the service worker, otherwise it's a risk to the user's privacy and battery. APIs like Background Fetch, that can let you avoid the service worker's termination.
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.
- 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.
Service workers tasks are generally more related to acting as a network proxy, handling background tasks, and things like caching and offline.
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.
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.
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 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.
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.