An introduction to service workers in Javascript

Service Worker, the holy grail of PWA or Progressive Web Applications. But, what’s is a PWA?.

Google says something little specific, they are “experiences”, others call “regular web applications”, others “like native application” but for all of you the easy explanation it’s that a PWA is a web application with, at least, a service worker with cache.

Time ago, all of you have seen kind of webs (for example a chat) that communicate with a server and returns (and send) text to a group of people. Or, before frameworks like AngularJS (or angular, or the typical component framework or library) you have seen background execution of code inside your browser.

Web worker service

That was a web worker service. A web service is a piece of code that you can run on your browser at the background and you can communicate to sending and receiving “messages”. And they was “complicated of use” until the creation of this frameworks or libraries.

For example, a typical web worker for knowing primer numbers will be like this:

The HTML file:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Prime number</title>
  </head>
  <body>
    <h1>Prime number worker</h1>
    <article>
      Primer numerb!: 
      <strong><div id="result"></div></strong>
    </article>
    <script>
    var worker = new Worker('calculateprime.js')
    worker.onmessage = function (event) {
      document.getElementById('div').textContent = event.data;
    };
    </script>
  </body>
</html>

The javascript file that refer (calculateprime.js):

var n = 1;
search: while (true) {
  n += 1;
  for (var i = 2; i <=n; i += 1) {
    if (n % i == 0) {
        continue search;
    }
    postMessage(n);
  }
}

When you run the index file on a browser you will se how it shows prime numbers one after other. Easy. But the most important it's that you can see how on the index.html file we create a worker that relays on the javascript file and we take the method "onmessage" that works when the calculateprime.js send a message via postMessage. That's how a worker and the "client" pass messages from one to another (and it can be anything, not only a number or an string, it can be an array or an object).

The worker run with the page but in background, in a separate threat. And that it's the important way.

A Service Worker

Well, after you see this brief example of a web worker you can imagine how this javascript frameworks and libraries work (or at least what it's under the hood).

But if you give a twist to this you can thing what a PWA it's or, at least, what it's the principal element of a PWA: a service worker.

With the same concept (at least) we can create a web worker that do something more complicated knowing that modern browsers have something like a storage (for files, sessions), where you can put "things" on it, a web SQL (like a database) where you can create a database storage and so on, so why don't take advantage of this?.

If you create a worker that use the browser storage in order to put there files needed for your web application and you catch the fetch operations of your website serving the files from the cache when needed you can create an "offline" web app that will work "like a native app".

Let's see an example, first the HTML file:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Service Worker 101</title>
  </head>
  <body>

    <center><img></center>

    <script>if ('serviceWorker' in navigator) {
        window.addEventListener('load', function() {
          navigator.serviceWorker.register('service.js')
          .then(function(registration) {
           // Successful registration
           console.log('Hooray. Registration successful, scope is:', registration.scope);
          }).catch(function(err) {
           // Failed registration, service worker won’t be installed
           console.log('Whoops. Service worker registration failed, error:', err);
          });
        });
      }

      // Fetch example
      var Image = document.querySelector('img');

      fetch('flores.jpg')
      .then(function(response) {
        console.log('Fetching on the page ok!');
        return response.blob();
      })
      .then(function(myBlob) {
        var objectURL = URL.createObjectURL(myBlob);
        Image.src = objectURL;
      });

    </script>
  </body>
</html>

And now the service.js file:

// Vars
const PRECACHE = 'precache-v1';
const RUNTIME = 'runtime';
var CACHE_NAME = 'my-first-serviceworker';
var urlsToCache = [
  '//flores.jpg',
  'index.html',
];

self.addEventListener('install', function(event) {
  console.log('Installing!');
  event.waitUntil(
    caches.open(CACHE_NAME)
   .then(function(cache) {
     console.log('Addiding files to cache!');
     console.log(cache);
     // Open a cache and cache our files
     return cache.addAll(urlsToCache);
   })
   // Skip waiting if it's on the cache
   .then(self.skipWaiting())
  );
});

// Activate event
self.addEventListener('activate', function(event) {
  console.log('Activating event '+ event);
});

//Control fetch
self.addEventListener('fetch', function(event) {
    console.log('Fetching some data');
    console.log(event.request.url);
    event.respondWith(
        caches.match(event.request).then(function(response) {
            console.log('File is on the cache! Hooray! Let\'s take it from there!');
            return response || fetch(event.request);
        })
    );
});

It can be sound complicated but it's very easy. First let's see the html file. The first you will notice it's that there's an empty image tag, but if you can see the piece of javascript code you will see that this image it's completed by a fetch (the flores.jpg a simple image file with some flowers -in spanish-).

On the other way, you will se the way of a worker is installed. First you see if the 'navigator' (the web browser) has the ability of having a serviceworker. After that we call the method register with the js file. You can do it with a promise (then you will know if it's fail or not). Nothing more.

But if you see the javascript file you can see that there's three important things that show you how the service worker "works".

A service worker works thanks to adding events to "self". Remember that self means window (it have a large explication but the best it's that you think these).

The events (of the window) that you must take consideration are "install", "fetch" and "activate". That are the most important events that a service worker fires.

For example, on our example, on the install event we create the cache and we put the files on it (they are on the array on the variables) thanks to defining what kind of cache we use. It's auto explicative. It fires once when it runs on the first time.

On the activate event, we usually take care of the cache if there's any more modern file of the service worker or the cache files. It runs everytime the service worker activate.

And the last important one its the fetch event. It runs when the browser (the window) makes a fetch. On the example tries to find the file on the cache and serve from it if exists or do the fetch if not. It runs on every fetch.

So, in this way we have created a cache that, if you are not connected to the network, visualice the jpg file or run faster that taking the file from the network (on the second time, of course).

There's more events that will help you to controle your service worker and I recomend you to take a look at this github repository where you can find more examples and inspiration on how it can be done https://github.com/GoogleChrome/samples/tree/gh-pages/service-worker

And this is a typical service worker. I have put several console.log to see whats happening in order to you learn more about knowing what's happening 🙂

Of course you can do many more things with it but this is a brief introduction on it.

Originaly posted on my Linkedin.

Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.