Service workers are awesome

Published on: March 29, 2015

In the war between native and web apps there's a few aspects that make a native app superior to a web app. Among these are features like push notifications and offline caching. A native app, once installed, is capable of providing the user with a cache of older content (possibly updated in the background) while it's fetching new, fresh content. This is a great way to avoid loading times for content and it's something that browser vendors tried to solve with cache manifests and AppCache. Everybody who has tried to implement offline caching for their webpages will know that the AppCache manifest files are a nightmare to maintain and that they're pretty mysterious about how and when they store things. And so the gap between native and web remained unchanged.

In comes the service worker

The service worker aims to solve all of our issues with this native vs. web gap. The service worker will allow us to have a very granular controlled cache, which is great. It will also allow us to send push notifications, receive background updates and at the end of this talk Jake Archibald mentions that the Chrome team is even working on providing stuff like geofencing through the service worker API. This leads me to think that the service worker just might become the glue between the browser and the native platform that we might need to close the gap once and for all.

If you watch the talk by Jake Archibald you'll see that the service worker can help a great deal with speeding up page loads. You'll be able to serve cached content to your users first and then add new content later on. You're able to control the caching of images that aren't even on your own servers. And more importantly, this method of caching is superior to browser caching because it will allow for true offline access and you can control the cache yourself. This means that the browser won't just delete your cached data whenever it feels the need to do so.

How the service worker works

When you want to use a service worker you have to install it first. You can do this by calling the register function on navigator.serviceWorker . This will attempt to install the service worker for your page. This is usually the moment where you'll want to cache some static assets. If this succeeds the service worker is installed. If this fails the service worker will attempt another install the next time the page is loaded, your page won't be messed up if the installation fails.

Once the service worker is installed you can tap into network requests and respond with cached resources before the browser goes to the network. For example, the browser wants to request /static/style.css . The service worker will be notified through the fetch event and you can either respond with a cached resource or allow the browser to go out and fetch the resource.

HTTPS only!!

Because the server worker is such a powerful API it will only be available through HTTPS when you use it in production. When you're on localhost HTTP will do but otherwise you are required to use HTTPS. This is to prevents man-in-the-middle attacks. Also, when you're developing locally you can't use the file:// protocol, you will have to set up a local webserver. If you're struggling with that, I wrote this post that illustrated three ways to quickly set up an HTTP server on your local machine. When you want to publish a demo you can use github pages, these are server over HTTPS by default so service workers will work there.

A basic server worker example

Browser support

Before I start with the example I want to mention that currently Chrome is the only browser that supports service workers. I believe Firefox is working hard to make an implementation happen as well and the other vendors are vague about supporting the service worker for now. This page has a good overview of how far the completion of service workers is.

The example

The best way to illustrate the powers of the service worker probably is to set up a quick demo. We're going to create a page that has ten pretty huge pictures on it, these pictures will be loaded from several resources because I just typed 'space' in to Google and picked a bunch of images there that I wanted to include on a webpage.

When I load this page without a service worker all the images will be fetched from the server, which can be pretty slow considering that we're using giant space images. Let's speeds things up. First create an app.js  file and include that in your page html right before the body tag closes. In that file you'll need the following script:

navigator.serviceWorker.register('worker.js')
    .then(function(){
        console.log("success!");
    },
    function(){
        console.log("failure..");
    });

This code snipper registers a service worker for our website. The register function returns a promise and when it gets resolved we just log the words 'success' for now. On error we'll log failure. Now let's set up the service worker.

// You will need this polyfill, at least on Chrome 41 and older.
importScripts('serviceworker-cache-polyfill.js');

var files_to_cache = [
    // an array of file locations we want to cache
];

this.addEventListener('install', function(evt){
    evt.waitUntil(
        caches.open("SPACE_CACHE")
            .then(function(cache){
                console.log("cache opened");
                return cache.addAll(files_to_cache);
            })
        );
    });

The code above creates a new service worker that adds a list of files to the "SPACE_CACHE" . The install eventHandler will wait for this operation to complete before it returns a success status, so if this fails the installation will fail as well.

Now let's write the fetch handler so we can respond with our freshly cached resources.

this.addEventListener('fetch', function(evt){
    evt.respondWith(
        caches.open("SPACE_CACHE").then(function(cache){
            return cache.match(evt.request).then(function (response) {
                return response || fetch(event.request.clone());
            });
        })
    );
});

This handler will take a request and match it against the SPACE_CACHE. When it finds a valid response, it will respond with it. Otherwise we will use the fetch API that is available in service workers to load the request and we respond with that. This example is pretty straightforward and probably a lot more simple than what you might use in the real world.

Debugging

Debugging service workers is far from ideal, but it's doable. In Chrome you can load chrome://serviceworker-internals/ or chrome://inspect/#service-workers to gain some insights on what is going on with your service workers. However, the Chrome team can still improve a lot when it comes to debugging service workers. When they fail to install properly because you're not using the cache polyfill for instance, the worker will return a successful installation after which the worker will be terminated without any error messages. This is very confusing and caused me quite a headache when I was first trying service workers.

Moving further with service workers

If you think service workers are interesting I suggest that you check out some examples and posts online. Jake Archibald wrote the <a href="http://jakearchibald.com/2014/offline-cookbook/" target="_blank">offline cookbook</a>. There's many information on service workers in there. You can also check out his <a href="https://github.com/jakearchibald/simple-serviceworker-tutorial" target="_blank">simple-serviceworker-tutorial</a> on Github, I learned a lot from that.

In the near future the Chrome team will be adding things like push notifications and geofencing to service workers so I think it's worth the effort to have a look at them right now because that will really put you in the lead when service workers hit the mainstream of developers and projects.

If you feel like I made some terrible mistakes in my overview of service workers or if you have something to tell me about them, please go ahead and hit me up on Twitter.

The source code for this blog post can be found on Github.

Categories

Uncategorized

Subscribe to my newsletter