JavaScript Async - Service Workers
Written by Ian Elliot   
Monday, 07 September 2020
Article Index
JavaScript Async - Service Workers
Fetch Event
Awaiting Cache
Managing Service Workers

ServiceWorkers are the key ingredient you need to implement PWAs - Progressive Web Apps. In this extract from my recent book on JavaScript Async we put the pieces together and show how to use a ServiceWorker.

This is an extract from the recently published JavaScript Async: Events Callbacks, Promises & Async/Await.

Now Available as a Print Book:

 JavaScript Async

cover

You can buy it from: Amazon

Contents

  1. Modern JavaScript (Book Only)
  2. Events,Standard & Custom
  3. The Callback
      extract - The Callback & The Controller
  4. Custom Async - setTimeout, sendMessage & yield
      extract - Custom Async
      extract - Avoiding State With Yield 
  5. Worker Threads
      extract - Advanced Worker Threads 
  6. Consuming Promises 
  7. Producing Promises
      extract - The Revealing Constructor Pattern
     
    extract - Composing Promises 
  8. The Dispatch Queue
      extract - Microtasks
  9. Async & Await
      extract -  Basic Async & Await
      extract -  DoEvents & Microtasks 
  10. Fetch, Cache & Service Worker
      extract - Fetch  
      extract - Cache
     
    extract -  Service Workers ***NEW

Fetch, Cache and ServiceWorker are three key components in a new approach to web apps – the progressive web app or PWA. There are a number of libraries that make use of ServiceWorker, perhaps most notably Angular CLI, but you don’t have to adopt a library or framework to get the advantage of using ServiceWorker.

Included in chapter but not in this extract:

  • Basic Fetch
  • Request Object
  • FormData, Get & Post
  • Cache

The Service Worker

The fetch and cache API are general and can be used by the UI thread or by a Worker thread, but they are both very much connected with the new Service Worker. This promises to make HTML-based applications behave much more like desktop applications. It is, some think, the solution for which we have been looking for a long time.

What is so special about the Service Worker?

The first is that it has a life all of its own separate from the web page that it is associated with. Service Workers are downloaded, installed and activated. From then on the Service Worker is downloaded at least every 24 hours. Any page that the user opens that is within the Service Worker’s scope results in it being run – even if the page doesn’t reference it. This is very strange behavior for a JavaScript program. It behaves more like a browser add-on than a script in a web page.

The second is that a Service Worker intercepts all resource requests from the pages it is associated with – those that are within its scope. That is, if the page requests a resource via a particular URL the Service Worker intercepts this request and can either supply its own response or it can allow the resource to be downloaded by the browser in the usual way. Typically the Service Worker might use a Cache object to try to retrieve the resource and only fall back to the network if it is unavailable or if a fresher version is desirable. This approach is often referred to as “offline first” as it allows the app to operate even when the network isn’t available, however, there are many other use cases.

Notice that after the Service Worker has been installed it is invoked every time the browser fetches a resource that is associated with the Service Worker. The Service Worker doesn’t have to be re-installed or reloaded from the original page. Once installed the browser manages it and activates it as needed. In fact one of the problems you first encounter is how to stop a Service Worker once you have started it. In general management of Service Workers is one of the more difficult aspects of using them.

As for the Cache API, Service Workers are restricted to HTTPS for security reasons. You can test your code using a localhost-based HTTP server in Chrome and Firefox.

Registering Service Workers

A Service Worker is just a special type of Web Worker and it runs in its own global context with its own thread and has no access to the DOM. You can use the postMessage method to trigger message events in the UI thread, and so on. The first difference is that you have to register a Service Worker using the register method. You have to supply the name of the file containing the code and an option object which currently supplies the scope of the URLs that are going to be associated with the Service Worker.

The default scope is the location of the Service Worker and all subfolders. The optional scope can only narrow the range of URLs that that the Service Worker will handle. Also notice that any resource requested by a page within the scope of a Service Worker is also passed to the Service Worker.

Another subtle point is that the Service Worker is only registered after the page that is registering it has finished loading. From this point on, however, it intercepts all URLs in its scope including the one for the page that loaded it if this is in the scope. What this means is that a Service Worker cannot modify the resources being loaded by the page that registers it as it is loading.

So, for example, to register a Service Worker to manage traffic to all of the URLs in the site you would use:

async function reg() {
 await navigator.serviceWorker.register("\myWorker.js");
}

That is, the Service Worker is stored in the root of the site and so all of the site’s URLs are within its scope.

If myWorker.js contained:

console.log("loaded");

then you would see loaded displayed in the console when the reg function is called. Of course in practice you would have to check that serviceWorker was supported and handle any registration errors, but all this is omitted in the examples for clarity.

The register method returns a ServiceWorkerRegistration object which has a range of useful status indicating properties:

  • scope – the scope

and three state indicators:

  • installing

  • waiting

and

  • active.

Notice that you don’t have to check to see if a Service Worker is already registered. You can use the register method as often as you like and the browser will keep track of the latest version registered.

After a successful registration the Service Worker is available for use – it is active. Every time a page within its scope is loaded it is run. The browser also downloads the file if it is online and checks to see if the code has changed. If the code has changed by even a single byte the new version of the Service Worker is installed. However, it then enters the waiting state until the current Service Worker has finished being used. This means that all of the pages that are in scope have to be closed. When a page that is in scope is next opened the Service Worker enters the active state and starts to process requests.

There are also events associated with the state changes which we will look at later. It is also possible for a waiting Service Worker to force a takeover of the pages that are being processed by the older version.

What all this means is that once a Service Worker has been registered to a browser it becomes a permanent part of the web site defined by its scope. The Service Worker persists across browser and machine restarts. There is no need to register the Service Worker more than once, but if you do it does no harm.



Last Updated ( Monday, 07 September 2020 )