An introduction to service workers

Put simply a service worker is a script that runs in the browser. They can intercept network traffic, send push notifications and perform background syncs. If that was a bit of a blur don’t worry, we’ll take a step back and take a look at each in turn. 

 

Intercepting Network Traffic

Service workers can act as a proxy server between the browser and the network.  Which simply means that http requests can be intercepted by the service worker. One use case of a proxy server is to return cached pages when a network request fails.  

 

Push Notifications 

Push notifications are messages that can be displayed on a user’s device even when the app isn’t open. They can be used to present the user with brief prompts.  

  

 

Background Sync 

When your app is offline any data sent to your server will be lost. An emerging solution to this problem is background sync, which defers sending the data until the connection is restored. 

 

Important Concepts 

Service workers run in the background on a separate thread from the user interface which means that they do not have access to the DOM and as such cannot update it. The benefit to running in a separate thread is that long running processes such as image decoding, fetching data from the server …etc can be performed without effecting the user interface. 

 

An example App 

As with most things an example can really help to understand a new concpt. We’ll use our newly gained knowledge about services workers  to produce a mock app that will render cached pages if the network fails. Below is a screenshot of what we’ll build.

Here’s an overview of files we will need to create. To help keep things clear I have also included the context in which they will be run. 

File  Functions  Context 
/index.html  render page, loading external resources (css, js)   webpage 
/sw.js  caching pages, renders cached pages when network requests fail  separate thread 
/app.js  logic for app  webpage 
/server.js  serve our application  server 

In order to use a service worker we must first register it. First we check to see if the browser supports service workers, If not supported the app will not crash, we simply don’t do anything further with the service worker. The user will still be able to use the app as expected, they just won’t be able to take advantage of offline support.  

On the other hand if service workers are supported by the browser we add an event listener to the window which will be triggered upon page load. This listener is responsible for registering our service worker file.  


if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('/sw.js').then(response => {
            console.log('registration successful', response.message);
            return navigator.serviceWorker.ready;
        }, error => {
            console.info(error.message);
        }).then(registration => console.log(registration));
    })
}

 

 

To confirm our service worker is installed we can open chromes developer tools and view the application tab. Note the status is ‘activated’  

 

At this stage our app has a service worker installed but isn’t doing anything. If we check the offline checkbox and refresh the page, you’ll notice we are not serving a cached version of the page. 


In order to return cached pages in the event of network failure we must first cache the pages. The code below shows how this is achieved. First we register an event listener which will be triggered when the service worker is installed. Once the install event has fired we force it to wait until we have cached the ‘/index.html’ and ‘/app.js’ files using the caches API. 


const cacheName = 'v1';
const cacheAssets = [
    '/index.html',
    '/app.js',
];

self.addEventListener('install', e => {
    e.waitUntil(
        caches
            .open(cacheName)
            .then(cache => cache
                .addAll(cacheAssets)
                .then(() => self.skipWaiting()))
    )
});

 

When we view the Cache Storage tab in chromes developer tools we can now see our cached version of the page. 

 

We have cached the pages but we are not using them. If the network were to fail now we would still see the error message. We need to intercept network requests and return the cached version of the page upon failure. To achieve this we register an event listener for the ‘fetch’ event. We then attach a catch handler to the fetch event, this will be triggered if the network is down or an error is encountered. Within our catch we call the match function on the caches API. This will either resolve to our cached page or if the requested page is not cached it will simply display the usual error message. 


self.addEventListener('fetch', e => {
  e.respondWith(fetch(e.request).catch(() => caches.match(e.request)))
});

 

Now when we check the offline checkbox the app serves the cached version of the page. 

 

 

Conclusion 

By now you should have a much better understanding of what service workers are, the new features they bring and how they can benefit your next app. If you’d like to try implementing your own service worker checkout the source code for this tutorial here.