Service Workers

July 6th, 2017 / placed in JavaScript. / Tags: , , .

“Rich offline experiences, periodic background syncs, push notifications—functionality that would normally require a native application—are coming to the web. Service workers provide the technical foundation that all these features rely on.” (Gaunt, 2016)

So what is this “Service Worker” thingy?

This article is meant to give a simple idea and introduction about that a service worker is. This blog post should help you setting up a basic web app with a service worker. So, what is a service worker exactly? A service worker is a script that your browser runs in the background, separate from a web page. It opens the door to features that don’t need a web page or user interaction once it’s installed in your browser.

One of the reasons why service workers are very powerful is that it gives you the ability to intercept and handle network requests. This is a very exciting feature because this way you are able to create offline experiences. It gives developers full control over the experience. Sounds familiar? Correct! Before the service worker there was another API that gave users an offline experience on the web called AppCache. But according to Jake Archibald, there are a number of gotcha’s with AppCache that exist as well as the fact that while the design works particularly well for single page web apps, it’s not so good with multi-page sites. Service workers have been designed to avoid these common pain points (Archibald, 2013).

Before I go into further details and technical specs, there are a few important things to know about service workers:
 –  Basically, it’s just a JavaScript Worker. Meaning, it can’t access the DOM directly. Instead, a service worker can communicate with the pages it controls by responding to messages sent via the postMessage interface, and those pages can manipulate the DOM if needed.
 –  A service worker is a programmable network proxy, allowing you to control how network requests from your page are handled.
 –  Service workers make use of JavaScript promises.

The registration, activation and installing process

Installing process
This stage marks the beginning of registration. It’s intended to allow to set up worker-specific resources such as offline caches. Here the install event is being fired.
– Use event.waitUntil() passing a promise to extend the installing stage until the promise is resolved.
– Use self.skipWaiting() anytime before activation to skip installed stage and directly jump to activating stage without waiting for currently controlled clients to close.

At this install process one is able to apply the cache-first strategy. So you are able to cache all necessary assets of your web app in this process.

There are no clients controlled by other workers. This stage is intended to allow the worker to finish the setup or clean other worker’s related resources like removing old caches. At this point the activate event is being executed.
– Use event.waitUntil() passing a promise to extend the activating stage until the promise is resolved.
– Use self.clients.claim() in the activate handler to start controlling all open clients without reloading them.

The service worker is now idle and waiting to either receive a functional event like fetch, sync or push. In this state it can also be terminated and/or replaced by a new service worker.

Registering a Service Worker in your web app

When we want to integrate a service worker inside an app we do it by registering it in your main.js or app.js JavaScript file. This is the entry point into using service workers. All you have to do is the following:

 –  The outer block performs a feature detection test to make sure service workers are supported before trying to register one.
 –  Next, we use the ServiceWorkerContainer.register() function to register the service worker for this site, which is just a JavaScript file residing inside our app (note this is the file’s URL relative to the origin, not the JS file that references it.)
 –  The scope parameter is optional, and can be used to specify the subset of your content that you want the service worker to control. In this case, we have specified ‘/sw-test/‘, which means all content under the app’s origin. If you leave it out, it will default to this value anyway, but we specified it here for illustration purposes.
 –  The .then() promise function is used to chain a success case onto our promise structure. When the promise resolves successfully, the code inside it executes.
 –  Finally, we chain a .catch() function onto the end that will run if the promise is rejected.

Install and activate

As we have seen in the schematic, and after we registered our service worker, the browser will attempt to install and then active the service worker for your site on its first load.

The install event is the place where you’d want to cache the looks of your app so the app can be used with it’s offline capabilities. This is done through the service worker’s storage API cache. This API works in a similar way to the browser’s standard cache, but it is specific to your domain. It persists until you tell it not to. Although this API is not compatible in every browser you can use IndexedDB instead.

 –  Here we add an basic install event listener to the service worker (hence this), and then chain a .waitUntil() method onto the event. This ensures that the service worker will not install until the code inside waitUntil() has successfully occurred.
 –  Inside waitUntil() we use the method to create a new cache called v1, which will be version 1 of our site resources cache. This returns a promise for a created cache; once resolved, we then call a function that calls addAll() on the created cache, which for its parameter takes an array of origin-relative URLs to all the resources you want to cache. This should be mainly contain your application interface elements.
 –  If the promise is rejected, the install fails, and the worker won’t do anything. This is ok, as you can fix your code and then try again the next time registration occurs.
 –  After a successful installation, the service worker activates.


Now we basically have a cached version of our web app that we can serve once there is no internet connection. When the user navigates to a different page or refreshes, the service worker will begin to receive fetch events, an example of which is below.

Here we’ve defined our fetch event and within event.respondWith(), we pass in a promise from caches.match(). This method looks at the request and finds any cached results from any of the caches your service worker created.

If we have a matching response, we return the cached value, otherwise we return the result of a call to fetch, which will make a network request and return the data if anything can be retrieved from the network.