Honza Malý25.4.2018Back

Offline in the browser (part 2): Service Workers

In Part I, we covered offline browser operations in general, gave you an idea of detection methods, and explained what AppCache is.

As my assessment suggests, AppCache answers some of your questions, but unfortunately raises quite a few others. Perhaps that’s why it is deprecated, and the recommendation is to turn to alternatives, in particular:

Service Workers

If you go back to the previous chapter, and the “AppCache API” section, it makes you think: Wouldn’t it be great to control the application cache directly? Through a programmable interface of some kind?

It’s not that easy if you think about it further. It requires a position in the browser outside DOM (kind of) and being able to tweak the way the browser works with documents. Such a script would have to reside and be executed somewhere “in between” the browser tabs.

The good news is: It’s possible! Let’s discover Service Workers (SW).

Service Workers: What is it about?

You’ll find a very good description at google developers, from which we derive the examples given below.

  • Service Worker is a JavaScript Worker, i.e. a chunk of code living in isolation, unable to access the DOM, using messaging (postMessage) as its primary communication tool.

  • At the same time, the Service Worker behaves like a programmable proxy that gives you complete control over requests coming to your website.

  • It has a short life-cycle and therefore is not suitable to store and manage data.

  • It uses the Promises concept.

Registration

This is how to register the code:

navigator.serviceWorker.register('/sw.js');

sw.js is the code of our SW, and the register returns a Promise – it is then possible to tell if the registration has been successful or not.

Most modern browsers give you options to explore registered Service Workers; in Chrome, just navigate to chrome://inspect/#service-workers.

Service Workers in Chrome
Exhibit: Service Workers in Chrome

Service Worker - phases and progress

In its code, the Service Worker works with events for which you define a response.

In the install phase, you carry out steps that are initialised once the SW runs for the first time. For example: if you want the offline experience, you have to take the relevant files and store them in the cache. That’s what you use the cache interface for:

var CACHE_NAME = 'your-cache-name';
var urlsToCache = [
  '/',
  '/styles/main.css',
  '/script/main.js'
];

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        return cache.addAll(urlsToCache);
      })
  );
});

(You can’t fail to notice that all functions used here work with the Promises concept to the full extent. It is necessary to get a full grasp of this concept to be able to write a functional Service Worker.)

Then there is the fetch phase. You call it anytime there is a HTTP network request.

In the most rudimentary case, let’s serve from the cache if we find the given source in there:

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {       
        if (response) {
          return response;
        }
        return fetch(event.request);
      }
    )
  );
});

Of course, you can try and make it more perfect. You have a complete, programmable interface available to you. That way you can classify requests based on event.request HTTP and, for example, decide to store them in the cache and then return the cached response. The above documentation from Google describes it very well.

Service Worker stages
Exhibit: Individual life cycles for a Service Worker. Source: Google Developers.

Service Worker and Update

Same as with AppCache, the following applies: If only one byte containing a SW definition changes, the browser tries to renew it, or to say precisely, it thinks it’s new.

What happens in that case is the execution of a new script and an initiation of the install phase.

Nevertheless, the old Service Worker keeps control over anything that downloads over fetch on the web. The new one shows as waiting.

The old Service Worker is not destroyed and not replaced by a new one until and unless all the pages opened in your web are closed (by physically closing the tab in the browser). Also, the new activate event starts once the new Service Worker takes control.

The activate event is important for you if you want to clean up after the old SW; for example, cleaning the cache. It can be resolved by callback linked to the activate event.

Service Workers: Peculiarities and problems

  • You need to have active HTTPS on your web site, even during development at the localhost. Google’s explanation is that Service Workers gives you a powerful weapon in your hands, and it is therefore necessary to defend against attacks by the man-in-the-middle method.

  • Browser support. You’ll find the current status at github pages by Jake Archibald and see for yourself that that’s not really optimal. The situation will change when they are adopted by Safari and Edge , in particular. Don’t ever expect it running on the good old Internet Explorer, though. Update - April 2018 - Safari is officially supporting it now.

  • Considering how isolated the Service Worker operation is, it is difficult to work with error messages. What helps in Chrome is to pause in the inspector in case of unhandled exceptions.

  • Neither credentials nor cookies are transferred through fetch, so you can’t link the operation of the SW to them in any way. They can only be by-passed by using credentials: "include" setting, when you are calling fetch.

  • A few more nitty gritties - see this source.

Useful resources

I ran across this Service Worker Cookbook the other day. It is a library of practical examples of how you can use SW. From caching, you move on to more complex scenarios, for example operating Push notifications.

On the same note, I can recommend the Service Worker Recipes repository.

Real life

Here at Kurzor we don’t use the Service Workers on regular basis especially because of the missing support across browsers. And if we do, it’s mostly part of another tool that becomes a part of the package one way or another. For example, the SW Precache Webpack Plugin. At any rate, it would be foolish to depend on them in your app or on your website. You should only take them as a supplementary tool.

Unfortunately, we have also run across problems when someone deployed SW without giving it much thought or being aware of this technology at all. They reside in the browsers of website visitors where they can be quite mischievous, for example disabling web updates due to the cache getting stuck.

PRO TIP: If this happens to you, you’ll find this bullet-proof way to remove the SW from the website when the page is visited again - see the repository. The trick is that you call the “self-destroying” Service Worker that cleans up after itself and others.

Conclusion

We hope you have liked our short series and found sufficient explanations of the issues you face these days in offline browser operations and the things you can expect.

Honza Malý
maly@kurzor.net
+420 722 211 443
Honza specializes in interaction design and website development.

Part of Series: Go Offline

Offline in the browser (part 1): Detection, AppCache

Turn offline mode into an advantage and make your website or app ready for it - here's first part of our overview. In this article, you are going to find out which types of websites and apps are suitable for an offline mode. We are also going to describe the most frequent method these days: Application Cache.

Honza Malý4.1.2018

Send Us a Message