Service Workers, the future for caching
4 min read.
This week I got the opportunity to experiment with the Service Worker API at work, in a lab session held by @OskarKarlsson. Although it is not supported in all browsers just yet, my initial impressions are pretty much that I love it.
It feels a lot like what I would expect Application Cache to be but without the quirks. Not only can we use it as a “Cache Everything” solution, with service workers we’re also able to priorities how we want to read from the cache as well.
Example
Oskar helped us by providing a lot of boilerplate code, if you would like to see how it can be used with your project you can find some implementations using Angular JS over here.
I decided to spend my time at the session implementing the same example only working with vanilla JavaScript.
Promises play a pretty huge role in working with service workers. Likewise, the new Fetch API that is suppose to replace the XMLHttpRequest can also become pretty useful. This example contains a lot of both. If you want to know more about promises I’ve written about it before.
Implementation
In the example you can find the service worker implementation in the file called sw.js
. Good to know is that the service workers is somewhat path specific, meaning that if you want the service worker to be used for the whole domain, you will need to put the service worker in your domains root path.
In the service worker we are registering three events. Install
, activate
and fetch
. The first two are used for acting on Installing and Starting the Service Worker. In our case we are telling the service worker to cache all our application resources, much like the application cache would do.
function onInstall(event) {
event.waitUntil(
caches.open(APP_CACHE).then(function(cache) {
return cache.addAll(app);
})
);
}
The Service Worker works very much like a proxy, telling the web page where to get it’s content from. This is where the third event, fetch
, comes in. By responding to this event we can decide if we want to give the web page content from our cache or if we should let them use the network. We can then also cache the response that we get from the network to be used next time that resource is requested.
In our example you can we that we handle certain request differently. For the API call to reddit.com we are prioritizing network request, but for image requests and resource requests we are prioritizing cached data.
function onFetch(event) {
var requestUrl = new URL(event.request.url);
var response;
if(requestUrl.hostname === 'www.reddit.com') {
response = networkFallbackOnCache(event.request, API_CACHE);
} else if (requestUrl.pathname.split('.')[1] === 'gif') {
response = cacheFallbackOnNetworkFallbackOnOfflineImage(event.request, API_CACHE);
} else if(location.hostname === requestUrl.hostname) {
response = cacheFallbackOnNetworkFallbackOnOfflineImage(event.request, APP_CACHE);
}
return event.respondWith(response);
}
Offline
Because we cached all app resources when the Service Worker was installed we can now use the web page while being offline. We can also use the service worker for displaying alternative content if the client for some reason would lose connection in the middle of a session. In our example this is being done in the cacheFallbackOnNetworkFallbackOnOfflineImage
function were if no item is found in the cache, or if the network is down, we are showing an alternative image for any image requests.
Registering the Service Worker
Registering the service worker is simply done by the use of window.navigator.serviceWorker.register
and the use of Promises
// from js/main.js
if('serviceWorker' in window.navigator) {
window.navigator.serviceWorker.register('/sw.js')
.then(onSWRegisterSuccess)
.catch(onSWRegisterError);
}
Debugging
While working on this I found two tools useful for debugging the Service Worker while developing it. Those are chrome://inspect/#service-workers and chrome://serviceworker-internals. These can be used for inspecting the service worker code while running as well as starting/stopping and also unregistering the Service Worker.
The last one is useful when you want to test any updates to your service worker (you also might need to close all chrome tabs that are using the service worker for any changes to take effect).
More info
If you want to know more about Service Workers I’ve found this post on HTML 5 Rocks. Or you can watch this presentation from last years Google I/O.
You can also follow the progress of implementations in different browsers over at https://jakearchibald.github.io/isserviceworkerready/.