JS Service Workers — An Introduction

JS Service Workers — An Introduction

A journey towards taking your web application offline

The Why

You have created an awesome website and ready to host it for everyone to find. You have taken the pain of making it responsive and it looks great on all standard devices. Let us walk through a hypothetical Developer vs Product Manager conversation:

PM: Is this application responsive?

Dev: Yes boss!

PM: Does it work when he is on a slow internet?

Dev: Well, I have minimized the assets, serving them up gzipped, so its optimised, but might take a while to load.

PM: What if he goes offline?

Dev: (Dumbfounded)Web… Web application? Why would someone use a web application offline? We need to create apps if we want to provide offline capabilities!

The next part of the conversation might not be very pleasant, so let us skip that. What if I told you your web app can be taken offline? What if I told you that your app can act like a native application in multiple platforms and be pinned to the Start screen for easy access? Welcome to PWA, welcome to the future (not really, it has been around for a while. While Google introduced PWA in 2015, Steve Jobs conceptualized it back in 2007!)

What does it take?

One word: JavaScript

If you are a front end developer, you are already aware of JS, and its nitty-gritty details. You don’t have to learn Java, you don’t have to download software to develop an android app, and yet another for iOS. You don’t have to take the trouble of publishing it across multiple stores. You simply build upon your already existing awesome webpage.

Google Lighthouse

image.png

Have you seen the lighthouse tab (it would come as an audit on older versions) on Chrome developer tools? Go to any site and run it. You see a couple of metrics — how well does the site perform, does it take into account accessibility, does it follow best practices, and finally does it have what it takes to be a PWA? This report then has details on how to improve upon the site and is a great place to start enhancing your application.

What is a Service Worker

A service worker is a script that your browser runs in the background, separate from a web page, opening the door to features that don’t need a web page or user interaction. — Matt Gaunt on Web Fundamentals

image.png Service Worker Overview from Introduction to Service Workers by Google

Let us now dissect the definition:

A service worker is a script — It is composed of JavaScript. Also note, it is a worker and hence cannot access DOM elements [No more alert(“hello!”)], but has to communicate using messages.

Runs in the background — It is run on a different thread than the main JS even loop. Note that it might be terminated when not in use, and restarted when required, hence using global variables is a very bad idea.

Opening the door to features — It is a programmable network proxy and can intercept requests, respond on behalf of server and cache resources.

Don’t need a web page or user interaction — Push notifications, background sync, payment updates — service works bring a lot of handy features to the web platform.

Service Worker Life Cycle

The lifecycle of a service worker is not coupled with the web page. It starts with the first load of the page but persists long after the browser tab is closed.

image.png Service Worker Life Cycle by Matt Gaunt on Web Fundamentals

The first step of registering a service worker is the install step. Typically, assets are downloaded and cached in this step. If all goes well, it moves to the activated step, else moves to the error state. Once activated, it is ready to intercept requests and process messages. Typically, old caches are invalidated here. When there is no activity, it remains idle, and after a few seconds of idling, is terminated. No need to be alarmed, it is brought back to life when a new event occurs, like a request or message. Note that since the worker can be terminated at any point, using global variables is heavily discouraged. IndexedDB can be used to persist data, including cache. The service worker is terminated after some seconds of inactivity to ensure that they don’t live in memory, draining resources.

Service workers are extensively asynchronous and use Promises. No blocking actions are allowed in the worker, failing which it will be stopped.

Can I use Service Workers?

Web developers have lost a lot of their hairs owing to browsers deviating from web standards, it is only natural to have this question at the back of your head while reading through this article. The short answer is no. While some do not support it all together, some allow limited features. The amount of storage also varies from platform to platform and from browser to browser.

image.png Browser compatibility of Service Worker, screenshot from caniuse.com

Hence it is a good practice to do a feature test before installing a service worker. We should not have core functionalities in service workers as it might be executed in an incompatible platform-browser combination.

Another factor to keep in mind is that Service Workers can be used only on HTTPS-enabled websites. While it works on HTTP through localhost, it will not work in an insecure site. Service workers are powerful, and “with great power, comes great responsibility”. HTTPS ensures that the source of the script is the hosted site and protects against man-in-the-middle attacks.

Time to Code

Time to whip up some throwaway code! We will start with a simple HTML file with some script. We have a div with an id that we will change. We first check if we can rester a service worker, and if we can, go ahead to register it. Once it is registered, the DOM is updated. We also have logging of errors and meaningful messages to the DOM if something fails.

index.html

<!doctype html>
<title>First Service Worker</title>

<h1>Service Worker Test</h1>
<div id="response"></div>

<script>
// chcek if the browser supports service workers
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register("serviceworker.js",
        { scope: "/" })
        .then( registration => {
            // registration is an handle of the worker.
            // More event listeners can be attached or messages can be sent to this.
            // For this example, we just change the DOM to display message
            document.getElementById("response").innerHTML = 
                "Service Worker registered properly";
        })
        .catch( error => {
            // Update DOM and log error in case resgistration fails
            document.getElementById("response").innerHTML = 
                "Service Worker NOT registered";
            console.error(error);
        });
} else {
    // UPdate DOM to reflect service workers cannot be used
    document.getElementById("response").innerHTML = 
                "Service Worker NOT available";
}
</script>

serviceworker.js

console.log("We are a service worker");

// This is called once the worker is installed
self.addEventListener("install", event => {
   console.log("Install event");
});

// This is called once the worker becomes active
self.addEventListener("activate", event => {
   console.log("Activate event");
});

The serviceworker.js file is a barebone for now. We have logs to illustrate the flow. First, it is installed and then activated.

Let me walk you through on how to run this. You can get both the files from the above gist, but if you open the file in a browser it will not work. You’ll need to serve up the contents. The easiest way is to use the npm package called “serve” (assuming you have NodeJS development environment setup). You can navigate to the folder where you have these files and execute the following:

npm i -g serve
serve

This will start serving the contents of the folder at port 5000. If you click on the link http://localhost:5000 you’ll see the service worker is registered properly. If you click the network link (the one below local), the service worker will not work (remember HTTPS). In the debugger, you can see the details, as shown below, in the application tab. When you are developing, ensure that “Update on reload” is checked so that changes that you make to your worker are picked up. By default, a service worker once installed will always remain installed (but more on this later).

image.png Chrome Debug tool to inspect service worker

Coming up

This is the first of the series. More on service workers would be posted in the coming weeks. In the meantime, this piece sites a lot of resources that you can explore. Stay tuned to keep learning!

Did you find this article valuable?

Support Niladri Roy by becoming a sponsor. Any amount is appreciated!