Chimezie Enyinnaya
Service workers play a very vital role in Progressive Web Apps (PWA), as they are responsible for offline caching, push notifications, background sync etc. In this article, we’ll be demystifying the service worker lifecycle and what can be done at each stage of the lifecycle.
For effective use of service worker, an understanding of the service lifecycle is essential. The service worker lifecycle consists of mainly 3 phases, which are:
Let’s go over each of them.
A service worker is basically a JavaScript file. One thing that differentiate a service worker file from a normal JavaScript file, is that a service worker runs in the background, off the browser’s main UI thread. Before we can start using service worker, we must register it as a background process. This is the first phase of the lifecycle. Since service workers are not yet supported in all browsers, we must first check to make sure the browser supports service workers. Below is a code we can use to register a service worker:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(function (registration) {
console.log('Service worker registered!');
})
.catch(function (err) {
console.log('Registration failed!');
})
}
First, we check if the browser supports service workers, that is, if the navigator
object has a serviceWorker
property. Only when it’s supported would we register the service worker. The register()
method takes the path to the service worker script and returns a promise.
At the point of registering a service worker, we can also define the scope of the service worker. The scope of a service worker determines the pages that the service worker can control. By default, the scope is defined by the location of the service worker script.
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js', {
scope: '/blog/'
})
.then(function (registration) {
console.log('Service worker registered!');
})
.catch(function (err) {
console.log('Registration failed!');
})
}
In addition to accepting the path to the service worker script, the register()
method can also accept an optional object, where we can define the scope
of the service worker. Here, we define the scope of the service worker to /blog/
, which will limit the service worker to only the blog
directory.
The fact that a service worker has been successfully registered doesn’t mean it has been installed. That’s where the installation phase of the lifecycle comes into play. Upon successful registration of the service worker, the script is downloaded and then the browser will attempt to install the service worker. The service worker will only be installed in either of these cases:
Once a service worker has been installed, an install
event is fired. We can listen for this event and perform some application0-specific tasks. For example, we could cache our application’s static assets at this point:
const assetsToCache = [
'/index.html',
'/about.html',
'/css/app.css',
'/js/app.js',
]
self.addEventListener('install', function (event) {
event.waitUntil(
caches.open('staticAssetsCache').then(function (cache) {
return cache.addAll(assetsToCache);
})
);
});
Here, we are using the open()
method of the Cache API, which accepts the name of the cache (staticAssetsCache
in this case) to either open (if it already exists) or create and returns a promise. Once the promise is resolved, that is, inside the then()
, we again make use of the addAll()
of the Cache API, which accepts an array of URLs to cache. Since the open()
method will return a promise, we need wrap it inside event.waitUntil()
, which will delay the installation of the service worker untill the promise is resolved. If the promise is rejected, the install
event fails and the service worker will be discarded.
If the installation was successful, the service worker enters an installed
state (though not yet active), during which it waits to take control of the page from the current service worker. It then moves on to the next phase in the lifecycle, which is the activation phase. A service worker is not immediately activated upon installation. A service worker will only be active (that is, be activated) in any of these cases:
self.skipWaiting()
is called in the install
event handler of the service worker scriptAn example of using the skipWaiting()
method to activate a service worker can look like below:
self.addEventListener('install', function (event) {
self.skipWaiting();
event.waitUntil(
// static assets caching
);
});
An activate
event is fired upon a service worker being active. Like the install
event, we could also listen for the activate
event and perform some application specific tasks. For for example, clearing out the cache:
const cacheVersion = 'v1';
self.addEventListener('activate', function (event) {
event.waitUntil(
caches.keys().then(function (cacheNames) {
cacheNames.map(function (cacheName) {
if (cacheName.indexOf(cacheVersion) < 0) {
return caches.delete(cacheName);
}
});
});
})
);
});
The snippet above loops through all the named caches and deletes any existing if the cache does not belongs to the current service worker.
Once the service worker has been activated, it now has full control of the pages. With the service worker active, it can now handle events such as fetch
, push
and sync
.
self.addEventListener('fetch', function (event) {
event.respondWith(caches.match(event.request))
.then(function (response) {
return response || fetch(event.request);
});
});
If the service worker after being active, does not receive any of the functional events mentioned above, it goes into an idle
state. After being idle for some time, the service worker goes into a terminated
state. This does not mean the service worker has been uninstalled or unregistered. In fact, the service worker will become idle again as soon as it begins to receive the fuctional events.
Below is a visual summary of the service worker lifecycle:
In this article, we looked at the service worker lifecycle, the events that are emitted at end phase of the lifecycle. Also, we looked some possible things we could with a service worker by hooking into some of these events.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Is this part wrong? In the “Activation” section, where you’re talking about when the service worker becomes activated, one of the mentioned cases is:
But I tried this by adding an event listener on the “activate” event, and it runs only once after the installation, and not anymore, specifically not after hitting subsequent refreshes… (Thanks for the article).