Service Workers for Dummies

Ivan Pandžić
NSoft
Published in
5 min readMar 19, 2019

Exciting times are ahead of the web. Offline experiences, push notifications, background sync and geofencing are coming to the web. All those features have one thing in common and it is the Service Worker — cornerstone for these advanced features.

What is Service Worker?

Service Worker (SW for short) is a programmable type of Web worker which acts as a proxy between browser and server. Once installed, it opens the possibility for features which don’t require web page or user interactivity. SW is developed to address the issues like:

  • caching and offline experiences
  • handling background synchronization and push notifications

Just like other types of web workers, it runs in a separate thread and JS context so we cannot access the DOM. Communication with the main thread is performed via postMessage API. Prerequisite for using SW is to serve the content via HTTPS, but for development, localhost is allowed.

Lifecycle

In order to start using the benefits of the SW, we need to install it first. Installation is triggered by calling the register method which downloads the worker script at given url and kicks off the installation.

SW Lifecycle

register can be called on every reload no matter whether the SW is installed or not. The browser will take care of the installation & update if necessary.

register method accepts relative path to script URL. SW location declares which subset will be controlled by it:

  • /sw.js — File served at the root URL will control whole app.
  • /test/sw.js — SW will control only pages served from /test/* url

Install & Activate

After registration — install and activate phases are next. SW is event driven so we can use eventListeners to run some tasks in install and activate phase. Both cycles are triggered only once per SW lifetime. Updating the existing SW browser considers it as a new SW which will get its own install and activate event. This is the usual time when files are cached and old files are cleared from the cache. We will address this later.

Controlling the pages

Once the activate event is fired, SW is ready to receive functional events, but it isn’t controlling the pages yet. Why? Because there wasn’t any SW first time when the page was loaded. We can log the navigator.serviceWorker.controller from console to see which SW is in control. If the result is null, then the page isn’t controlled yet. After next refresh document will be under SW’s control.

Functional events

Activated SW can receive functional events like: push, sync and fetch.

Fetch event

SW will receive fetch events for every HTTP request made in worker’s scope: app navigation, HTTP requests to a same and different origin. We can hijack those requests and manipulate them. This request intercepting is the reason why working with SW requires site served over HTTPS. Without HTTPS man-in-the-middle could exploit all those sensitive information.

In the example above — On every fetch event — cache is searched for this cached request. If there is a match, the response will be returned, otherwise, network request will be made and response returned.

This approach is known as cache first then network. It is usually used to serve commonly requested files such as app shell(runtime, css files, assets). Beware of this approach, if not handled properly we could end up with old content all the time because it would be served from cache.

Caching files

As mentioned earlier, install phase is ideal for caching new files.

Method event.waitUntil takes the promise and extends the install phase until the promise is resolved. Install phase will wait until the cache is opened (caches.open) and the files passed as an array of paths are cached(cache.addAll). If downloading some of the declared files fails, SW won’t be installed.

Updating a Service Worker

From time to time, installed SW will require an update. Every time when we navigate to the app or refresh the page, the browser will try to download the worker script and compare it to the installed one. If there is only byte difference — the browser will kick off the installation process for the new Worker, eventually replacing the old one. Update process flow:

  1. New SW receives install event. If installation was successful, new SW enters the waiting state. Waiting state means that SW is ready to take control, but old SW is still controlling the page. This way browser ensures that only one version of page is active. Old SW will take control of the page as long as we are having one tab active which displays the page under SW control. We must leave the page and return to it in order to see the new SW as active.
  2. Once your new SW takes control — activate event will be fired, and new SW will start controlling the pages.

Skip waiting

Newly installed SW will enter waiting phase until we leave the page and eventually return to it. We can skip the waiting phase by calling self.skipWaiting which will replace the currently active worker with newly installed. skipWaiting is usually called inside install event, but there isn’t a rule about it.

skipWaiting will force the newly installed SW to handle the fetch events which were previously handled by old SW. Backward compatibility is something which we should have in mind when dealing with skipWaiting.

Handling old cache

Activate event is commonly used to delete old cached files. Deleting old files isn’t mandatory, but it is highly recommended because browsers do have a limit for cache storage for every SW.

Here in activate phase we loop through all caches and delete those which aren’t named cache-v1.

Developing with ease

Development with SW is a bit painful but luckily dev tools provide some tools which address these issues.

Update on reload

Updating SW requires to leave the page and return to it in order to apply the update. Dev tools provide update on reload option. This will force download/install if there is a byte difference and skip the waiting phase. This way we will have a new version of SW on every refresh.

Update on reload

Skip waiting

If we don’t want to update on every reload, when new SW is in waiting phase, dev tools provide the option to manually skip the waiting phase.

Skip waiting

Shift+Reload

Shift reload or force refresh request will set the serviceWorker.controller to null and delete the origin cache.

If you wish to contribute, join us in our open source projects, or apply for a job at NSoft.

--

--