What Are Service Workers In Javascript and its benefits

What Are Service Workers?

Service Worker is a script that works on browser background without user interaction independently. Also, It resembles a proxy that works on the user side. With this script, you can track network traffic of the page, manage push notifications and develop “offline first” web applications with Cache API.

Basically, a service worker is nothing but a script that is run in the background and by the browser itself. However, it is completely separate from a web page and also offers a host of features that does not rely on the need for a web page. In fact, in today’s terms, we already have seen a number of such implementations such as push notifications, geo-fencing and background syncing.

In this tutorial, we shall discuss the core feature, which involves the interception and handling of network requests. This shall also include the ability to manage a cache of responses programmatically. Also, this is a very exciting API to work with since it allows for offline support and thus gives developers complete support over the complete experience.

Before service workers invaded the landscape, there was another way to achieve offline experience. This was possible by means of the App Cache. However, it has its own issues and also worked better for single page apps and not for sites with multiple pages. Service workers, so far, have taken care of these issues though.

 

Service Worker v/s Web Worker

Both Service workers and Web workers are JavaScript workers with lots of similarities but there are a few difference too.

Service workers are designed to handle network requests and assist in offline first development, Push Notifications and background syncs. Communication’s with the webpage must go through service workers PostMessage method.

Web workers mimics the multi-threading model, allowing complex / data or processor intensive tasks to run in background. Ideal to keep your UI jank free when you have to deal with a lot of computations. Communication’s with the webpage must go through web workers PostMessage method.

Service workers are also intended to be used for such things as:

  • Background data synchronization.
  • Responding to resource requests from other origins.
  • Receiving centralized updates to expensive-to-calculate data such as geolocation or gyroscope, so multiple pages can make use of one set of data.
  • Client-side compiling and dependency management of CoffeeScript, less, CJS/AMD modules, etc. for development purposes.
  • Hooks for background services.
  • Custom templating based on certain URL patterns.
  • Performance enhancements, for example pre-fetching resources that the user is likely to need in the near future, such as the next few pictures in a photo album.

In the future, service workers will be able to do a number of other useful things for the web platform that will bring it closer towards native app viability. Interestingly, other specifications can and will start to make use of the service worker context, for example:

  • Background synchronization: Start up a service worker even when no users are at the site, so caches can be updated, etc.
  • Reacting to push messages: Start up a service worker to send users a message to tell them new content is available.
  • Reacting to a particular time & date.
  • Entering a geo-fence.

 

The lifecycle of a service worker

As mentioned above, a service worker has a lifecycle that is in every way separate from a web page. In order to make use of a service worker, you will first need to register it to your website. This can be done by registering it in your page’s JavaScript and this allows the browser to start the worker installation step in the background. Generally, during the installation step, one can cache some static assets. If all the files happen to be cached successfully, the service worker stands installed. However, if there is any missing file during the cache, the installation step fails and also does not activate the service worker. In case this happens, do not worry, as it does try to install again the next time.

The installation step in followed by the activation step, where one can handle any of the old caches as well. After this, the service worker takes control over all the web pages that fall under its scope. Note that the page that registered the service worker isn’t controlled until the next time it is loaded.

A service worker will necessarily be in either of two states that it can exist in. One is the active state, where it handles messages and network requests through fetch and message events. The other is the terminated state, when it is not in use to save memory.

Following image is showing the life cycle of service worker

Service Worker Lifecycle

Service Worker lifecycle has 3 step; Registration, Installation, and Activation.

Registration

Service worker is a property of the navigator object of the window. You can use “register” function of serviceWorker to start the registration process. (Note: This function returns “Promise”)

Where should we use registration function?
You can start registration process every page load if you need. Because this process doesn’t start from the beginning every time. If service worker is the new or updated, the installation process will start.

To get started, you’ll first need to register the service worker in your page. You can do this in a top level JavaScript file, for example app.js. This lets the browser know where your service worker’s JavaScript file (sw.js) lives:

if ('serviceWorker' in navigator) {
    window.addEventListener('load', function() {
        navigator.serviceWorker.register('/sw.js').then(function(registration) {
            console.log('ServiceWorker registration successful with scope: ', registration.scope);
        }, function(err) {
            console.log('ServiceWorker registration failed: ', err);
        });
    });
}

The above example first checks to see if the service worker exists in the browser’s navigator. If so, then sw.js is registered. You must reload the page to complete the process. If you’re using Chrome DevTools, look under the service workers tab in the application panel to see if registration was successful.

 

Install a service worker

After a controlled page kicks off the registration process, let’s shift to the point of view of the service worker script, which handles the install event.

For the most basic example, you need to define a callback for the install event and decide which files you want to cache.

self.addEventListener('install', function(event) {
  // Perform install steps
});

Inside of our install callback, we need to take the following steps:

  1. Open a cache.
  2. Cache our files.
  3. 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);
      })
  );
});

Here you can see we call caches.open() with our desired cache name, after which we call cache.addAll() and pass in our array of files. This is a chain of promises (caches.open() and cache.addAll()). The event.waitUntil()method takes a promise and uses it to know how long installation takes, and whether it succeeded or not.

If all the files are successfully cached, then the service worker will be installed. If any of the files fail to download, then the install step will fail. This allows you to rely on having all the assets that you defined, but does mean you need to be careful with the list of files you decide to cache in the install step. Defining a long list of files will increase the chance that one file may fail to cache, leading to your service worker not getting installed.

This is just one example, you can perform other tasks in the install event or avoid setting an install event listener altogether.

 

Cache and return requests

Now that you’ve installed a service worker, you probably want to return one of your cached responses, right?

After a service worker is installed and the user navigates to a different page or refreshes, the service worker will begin to receive fetch events, an example of which is below.

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }
        return fetch(event.request);
      }
    )
  );
});

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. This is a simple example and uses any cached assets we cached during the install step.

If we want to cache new requests cumulatively, we can do so by handling the response of the fetch request and then adding it to the cache, like below.

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }

        return fetch(event.request).then(
          function(response) {
            // Check if we received a valid response
            if(!response || response.status !== 200 || response.type !== 'basic') {
              return response;
            }

            // IMPORTANT: Clone the response. A response is a stream
            // and because we want the browser to consume the response
            // as well as the cache consuming the response, we need
            // to clone it so we have two streams.
            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(event.request, responseToCache);
              });

            return response;
          }
        );
      })
    );
});

What we are doing is this:

  1. Add a callback to .then() on the fetch request.
  2. Once we get a response, we perform the following checks:
    1. Ensure the response is valid.
    2. Check the status is 200 on the response.
    3. Make sure the response type is basic, which indicates that it’s a request from our origin. This means that requests to third party assets aren’t cached as well.
  3. If we pass the checks, we clone the response. The reason for this is that because the response is a Stream, the body can only be consumed once. Since we want to return the response for the browser to use, as well as pass it to the cache to use, we need to clone it so we can send one to the browser and one to the cache.

 

Background Sync (Sync Event)

Background Sync is a Web API that provides to delay a process until Internet connection has stable. We can adapt this definition to the real world; there is an e-mail client application that works on the browser and we want to send an email with this tool. Internet connection is broken while we are writing e-mail content and we didn’t realize it. When completed the writing, we click send button.
Here is a job for the Background Sync.

In the following view shows classical process sending email to us. If the Internet Connection is broken, we can’t send any content to Mail Server.

In here, you can create any scenario for yourself. A sample is in the following for this case.

  1. When we click “send” button, e-mail content will be saved to IndexedDB.
  2. Background Sync registration.
  3. If the Internet connection is available, all e-mail content will be read and sent to Mail Server.
    If the Internet connection is unavailable, service worker waits until the connection is available even though the window is closed. When it is available, e-mail content will be sent to Mail Server.

 

Next steps in offline support

Edge and Firefox browsers are currently working on implementing background sync, which is fantastic. It is one of the best features for providing a more empathetic experience for users moving between internet connection and connection loss. Fortunately with a little help from online and offline events and IndexedDB, we can start providing a better experience for users today.

Your email address will not be published.