As long as there are humans on earth, places to travel to, and mobile devices in their hands, the need to be able to view content offline will remain, and the APIs created to tackle these issues will continue to progress along with the demand. The newest script hoping to support offline experiences and put the control in the hands of the developer is the Service Worker API and, luckily for developers, this API found solutions to most of the issues regarding its predecessor, AppCache, in the process.
What is a Service Worker?
A service worker is a script that your browser runs in the background that is not connected to a webpage. This allows you to utilize features that don’t need said webpage or user interaction such as background sync and push notifications and lets you serve content from a cache.
This is mostly effective for one of two types of websites: one where you are looking up information, such as Wikipedia, and ones where you are doing things such as Sprite Cow, if you are familiar with sprite sheets and the gaming industry, in particular. Similarly, if you are using something like Sprite Cow for gamification in education, this is a wonderful way to ensure that students can participate even if they are not able to access the internet at home.
Basically, a service worker is a programmable network proxy that allows the developer to control how the network requests from their page are handled. And, in turn, individuals in areas with zero connectivity are able to view information from your site and even send you messages without being denied the ability or seeing the spinning wheel of death in the process.
This API can help with features such as push notifications and background sync as well, which can allow users to interact with you despite their connectivity which increases the likelihood that these individuals will stay on the site and feel engaged. Similarly, it will give companies the ultimate connectivity flexibility, meaning it won’t let internet outages hurt small businesses or affect traveling employees needing to access important information without paying costly roaming fees likewise. Background sync, in particular, is a wonderful way to utilize Service Worker for UX as it not only helps with site performance by adding the message to output straight away. But also helps your users to send messages in the background once they get connectivity as opposed to receiving a ‘sorry, but you can’t leave just yet’ type of message when they try to close the tab or navigate away from the page.
Why is it Better Than AppCache?
There are multiple ways that the Service Worker API tackled the issues AppCache had that made it pretty frustrating to use. However, one of the most refreshing ways it blows its predecessor out of the water is its ability to be easily fixed if ever broken. AppCache, although it allowed you to specify assets to cache easily, made assumptions about what you were wanting to do and, in turn, it broke easily when those assumptions were not followed by your app to the tee. Although the Service Worker APIs syntax is more complex, you get to use Javascript to control your AppCache-implied behaviors with a fine degree of granularity. You can also set an app up to use cached assets first easily which then provides a default experience when offline as well.
Furthermore, although it was easy to set up AppCache and learn it, its underlying confounding nature as well as its lack of flexibility and assumptions that it made led to countless problems regarding breaking and, when it did, it was extremely difficult to fix and dysfunctional programmers, in particular, were extremely upset by its weak performance in the long run. Therefore, Service Worker is a wonderful alternative to AppCache for anyone with a site that would profit from having offline capabilities.
How to Install a Service Worker
The Installation process of a service worker is not as hard as you may think. The first step you will want to take is to kickstart the process by registering it in your page, which tells the browser where your service worker Javascript file lives. You’ll want to first see if the service worker is available and, if so, the service worker at /sw.js will be registered once the page is loaded.
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
For this method, we are putting the service worker file at the root of the domain meaning that the service worker’s scope will be the entire origin (i.e. the service worker will receive fetch events for everything on this domain). However, perhaps you only want the service worker’s scope to relate to particular pages. If this is the case, you can register the service worker file at /blank/sw.js and the service worker will only see fetch events for pages with an URL that begins with /blank/ (i.e. /blank/ilovepugs or /blank/redhatrocks).
Next, you can check that the service worker is successfully enabled by going to chrome://inspect /#service-workers and searching for your site.
After this is complete, you want to head over to the point of view of the service worker script that handles the install event, define a callback for the install event and determine which files you want to cache.
self.addEventListener('install', function(event) {
// Perform install steps
});
After this is done, you are going to want to open a cache, cache the desired files, and confirm whether all the required assets are cached or not.
var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
'/',
'/styles/main.css',
'/script/main.js'
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
Since Service Worker uses promises quite a bit, you can see above that you will call caches.open( ) and then cache.addALL( ) and pass in our array of files, which is a chain of promises (don’t worry, I’ll explain promises below for anyone wondering what it is).
Continuing with utilizing promises, you can use it to know how long installation will take and if it was successful by using the event.waitUntil ( ) method, as shown above.
If all of the files successfully are cached, then the installation process will be successful, however, if any of the files does not cache properly, the service worker installation will fail. This is why choosing a large number of files to cache can sometimes be a hassle.
Keep in mind, this is an example. There are multiple other tasks that you can perform in the Install event. And you also don’t even have to set and Install event listener if you don’t want to.
Next, you can cache and return requests by following these simple steps:
- Add a callback to .then( ) on the fetch request and wait until you get a response.
- Ensure the response is valid.
- Make sure the status is 200 on the response.
- Ensure that the response type is basic, meaning it is a request from our origin, and that requests to third party assets aren’t cached as well.
- If all of this is correct, clone the response due to the fact it is a Stream and the body can only be consumed once. By cloning it, it will send one to the browser and one to the cache.
How to Update Your Service Worker
Updating your service worker is important and, to do so, simply follow these steps:
- Update your service worker Javascript file. When the user navigates to your site, the browser tries to redownload the script file that defined the service worker in the background. If there is even a byte's difference in the service worker file compared to what it currently has, it considers it new and you go back to the spinning wheel of death.
- Once this is done, your new service worker will begin and the Install event will be fired. The old service worker will still be controlling the current pages then and the new service worker will enter a waiting state, however, when the currently open pages of your site are closed, the old will be killed and the new one will take its place.
- At this point, the new service worker’s activate event will be fired and you will have successfully updated your service worker.
The Cons of Service Workers
For this part of the article, I am going to begin with the worst and move to the best as I always like to end on a positive note. The cons of service workers are few and far between, however, knowing what they are and how to work around them, you can avoid another AppCache situation with this far more helpful and reliable API.
One of the first ones to address is that service worker is a Javascript Worker, which means that it cannot access the DOM directly and communicates with pages it controls by responding to messages sent via the responding interface (those pages can manipulate the DOM if needed). By responding to messages sent via the postMessage interface, in particular, you manipulate the DOM accordingly making this not entirely a con but still something to keep in mind.
Another con to service workers is that they can’t rely on global state within their onfetch and onmessage handlers because they are terminated when not in use and restarted when they are needed again. Therefore, if there is information that you need to persist and reuse across restarts, you’re kind of up the creek without a paddle as service workers do not have access to the IndexedDB API. However, there are ways to access IndexedDB using service worker and this forum on Stack Overflow can help you to do this, if necessary.
Lastly, service worker uses promises quite a bit, which can be difficult for anyone who does not understand what promises are or doesn’t use them frequently. However, basically, all you need to know about them to start is that a promise represents the eventual result of an asynchronous operation. Even if the actual value isn’t known until some time in the future when the operation is completed, in other words, promises provide a clean, flexible way to chain multiple async operations together without having to nest function calls inside callbacks. The two main downsides to this is that, if you don’t follow proper error handling processes, then your code can become very difficult to debug and, because service worker is fully async, APIs such as synchronous XHR and localStorage can’t be used inside a service worker (kind of a bummer, but I think we’ll survive).
With all of this said, it’s really not that bad in comparison to the pretty overwhelmingly negative impact AppCache often had, and the pros are even more astonishing, leaving the service worker API in a much better place than its predecessor even as young as it may be.
The Pros of Service Workers
On top of all of the pros mentioned above, there are still, even more, pros to utilizing service worker. As we discussed above, promises will definitely be something you’ll have to freshen up on before using this API and limit a few of the APIs you can use within the service worker due to it being fully async, however, promises does have one ‘promising’ upside that kind of turns things around. You can attach .then( ) to the end and include callbacks inside it for success, failure, etc. or insert .catch( ) on the end if you want to include a failure callback, which is definitely a bit more extensive than AppCache was.
Another pro to using service worker is that it runs across HTTPS for security reasons as you don’t want to have modified network requests open to man in the middle attacks. This is a pretty great way to ensure the security of service worker, which shows that the security measures were definitely put in place for this API from the start.
Similarly, the fact that it runs across HTTPS allows you to host experiments or demos on sites such as GitHub. Although some individuals have complained about needing to get a TLS certificate in order to add HTTPS to their server, it still remains an effective and useful way to promote security and work out the kinks on a demo ahead of time.
Lastly, if you happen to be using newer browsers such as Firefox Nightly, Chrome Canary, or Opera, service worker features are already enabled by default, and it has been speculated that this API will soon be used to display notifications to the user of specific events occurring in the background using other APIs that are compatible with fully asynchronous APIs.
The most commonly used new browser in association with service workers is Chrome Canary as it has been said to have the best developmental tool support and many individuals on the Chrome team also helped to drive the service worker specification. However, any of the above are proficient as well.
In the end, this new API is a wonderful tool for developers looking to control the experience users are having even when they are offline and, on top of this, it is continuing to prove that AppCache, its predecessor, and the many issues that came along with it are finally going to be a thing of the past. From there, the opportunities are endless, and we may finally see web apps that are just as efficient as native apps for use when traveling or offline in the process.
Red Hat Mobile Application Platform is available for download, and you can read more at Red Hat Mobile Application Platform
Last updated: March 20, 2023