How does JavaScript work: Service Workers, their lifecycle and use cases

原文 : How JavaScript works: Service Workers, their lifecycle and use cases

Translator: neal1991

welcome to star my articles-translator , providing you advanced articles translation. Any suggestion, please issue or contact me

LICENSE: MIT

This is series #11 devoted to exploring JavaScript and its building components. Along the way of identifying and describing the core elements, we also shared some rules of thumb we used to build SessionStack, a JavaScript application that utilizes powerful and high-performance features to help users view and reproduce flaws in their Web applications in real time.

If you missed the previous chapters, you can find them here:

  • How JavaScript works: An Overview of the engine, runtime, and Call stack (translated)

  • Inside Google’s V8 engine + 5 tips on how to write optimized code

  • How JavaScript works: Memory Management and How to Handle four common Memory Leaks (translated)

  • The event loop and the rise of Async programming + 5 ways to better coding with async/await

  • Deep dive into WebSockets and HTTP/2 with SSE + how to pick the right path

  • A comparison with WebAssembly + why in certain cases it’s better to use it over JavaScript

  • The building blocks of Web Workers + 5 cases when you should use them

As you probably already know, progressive Web apps are only going to grow in popularity because they aim to streamline the Web application user experience and create a native app-like experience rather than the look and feel of a browser.

One of the main requirements for building progressive Web applications is to make them very reliable for networking and loading – it can be used for uncertain or non-existent network conditions.

In this article, we’ll take a closer look at Service Workers: how they work and what you should care about. Finally, we listed some of the Service Workers features you should take advantage of and shared our team’s experience in the SessionStack.

An overview of the

If you want to understand all about Service Workers, you should read our blog about Web Workers.

Basically, a Service Worker is a Web Worker, and more specifically, it is like a Shared Worker:

  • The Serivice Worker runs in its own global scripting context
  • It is not bound to a specific Web page
  • It doesn’t have access to the DOM

One of the main reasons the Service Worker API is exciting is that it enables your web application to support an offline experience, giving developers full control over the flow.

The life cycle of the Service Worker

The life cycle of a Service worker is completely independent of your Web page. It consists of the following steps:

  • download
  • The installation
  • The activation

download

This is when the browser downloads the JS file containing the Service Worker.

The installation

To install a Service Worker for your Web application, you must first register it, which you can do in JavaScript code. When a Service Worker is registered, it prompts the browser to start the Service Worker installation step in the background.

By registering the Service Worker, you can tell the browser where your Service Worker JavaScript files are. Let’s look at the following code:

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

This code checks whether the Service Worker API is supported in the current environment. If so, register the /sw.js Service Worker.

You can call the register() method every time the page loads without worrying – the browser will determine if the Service Worker is registered and will handle it correctly.

An important detail of the register() method is the location of the Service Worker file. In this case, you can see that the Service Worker file is located at the root of the domain. This means that the scope of the Service Worker will be the entire source. In other words, the Service Worker will receive fetch events for everything in the domain (which we’ll discuss later). If we register the Service Worker file in /example/sw.js, the Service Worker will only see the URL as /example/ (i.e. /example/page1/, /example/page2/) page fetch event.

During the installation phase, it is a good idea to load and cache some static resources. After the resources are successfully cached, the Service Worker installation is complete. If not (load failed) – Service Worker will retry. Once installed, you will know that static resources are in the cache.

Questions about whether registration needs to happen after the loading event. This is not required, but it is definitely recommended.

Why is that? Let’s consider the first time a user accesses your web application. There is currently no Service Worker, and the browser cannot know in advance whether there will be a Service Worker that will eventually be installed. If a Service Worker is installed, the browser needs to spend extra CPU and memory for this extra thread, otherwise the browser will spend rendering the page.

On top of that, if you just install a Service Worker on your page, you risk lazy loading and rendering — rather than making the page available to your users as quickly as possible.

Note that this is important only for the first time you visit the page. Subsequent page access is not affected by Service Worker installation. Once the Service Worker is activated the first time a page is accessed, it can handle load/cache events for subsequent access to your Web application. This all makes sense, as it requires being prepared to handle limited network connections.

The activation

After the Service Worker is installed, the next step is to activate it. This step is a good opportunity to manage the previous cache.

Once activated, the Service Worker will start controlling all pages that belong to its scope. One interesting fact: the page that registers a Service Worker for the first time is not controlled until the page is loaded again. Once the Service Worker is under control, it will be in one of the following states:

  • It handles the FETCH and message events that occur when a network request or message is issued from the page
  • Aborted to save memory

The life cycle looks like this:

Process the installation in the Service Worker

After the page speeds up the registration process, let’s look at what happens in the Service Worker script, which handles installation events by adding event listeners to the Service Worker instance.

These are the steps you need to take to install event handling:

  • Open a cache
  • Cache our files
  • Verify that all requested resources are cached

Here is a simple installation process for a Service Worker:

var CACHE_NAME = 'my-web-app-cache';
var urlsToCache = [
  '/',
  '/styles/main.css',
  '/scripts/app.js',
  '/scripts/lib.js'
];

self.addEventListener('install', function(event) {
  // event.waitUntil takes a promise to know how
  // long the installation takes, and whether it 
  // succeeded or not.
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
});Copy the code

If all files are successfully cached, then the service worker is successfully installed. If either file download fails, the installation step will fail. So keep an eye on the files you put here.

Handling installation events is completely optional and you can avoid it so you don’t need to perform any of the steps here.

Caching requests at run time

This is the part that really needs to be dealt with. You’ll see here how requests are intercepted and returned to the created cache (or newly created request).

After the Service Worker is successfully installed, the Service Worker receives a FETCH event when the user browses another page or refreshes the current page. The following example shows how to return a cached resource or perform a new request to cache the result:

self.addEventListener('fetch', function(event) { event.respondWith( // This method looks at the request and // finds any cached results from any of the  // caches that the Service Worker has created. caches.match(event.request) .then(function(response) { // If a cache is hit, we can return thre response. if (response) { return response; } // Clone the request. A request is a stream and // can only be consumed once. Since we are consuming this // once by cache and once by the browser for fetch, we need // to clone the request. var fetchRequest = event.request.clone(); // A cache hasn't been hit so we need to perform a fetch, // which makes a network request and returns the data if // anything can be retrieved from the network. return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(! response || response.status ! == 200 || response.type ! == 'basic') { return response; } // Cloning the response since it's a stream as well. // 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) { // Add the request to the cache for future queries. cache.put(event.request, responseToCache); }); return response; }); })); });Copy the code

What happens in a NUTSHELL:

  • Event.respondwith () will determine how to respond to the FETCH event. We’ll pass a promise from caches.match() to listen for the request and see if there’s a hit in the cache.
  • If the cache exists, then the response is sent.
  • Otherwise fetch is performed
  • Check whether the status is 200. We also check that the type of response is basic, which also indicates that the request is cognate. Requests for third-party resources are not cached in this case.
  • The response is added to the cache.

Requests and responses must be cloned because they are streams. The body of the stream can only be consumed once. And as soon as we want to consume them, we want to clone them because browsers have to consume them.

Update Service Worker

When a user accesses your Web application, the browser will try to re-download the JS file containing your Service Worker. This will happen in the background.

If there is a one-byte difference between the downloaded Service Worker file and the current Service Worker file, the browser assumes that the change has taken place and starts a new Service Worker.

The new Service Worker will be started and the installation event will be triggered. However, at that point in time, the old Service Worker will still control your Web application, which means the new Service Worker will still be in a waiting state.

Once you close the page of your previously open Web application now, the old Service Worker will be aborted by the browser and the newly installed Service Worker will take over. The active event is triggered.

Why all of them? To avoid running two versions of a Web application in different tabs at the same time — which happens all the time on the Web and can be very buggy. (For example: you store different structures of data locally in the browser)

Delete data from the cache

The most common step in activating callbacks is cache management. You want to do this now because you are going to remove the old cache in the installation step, and the old Service Worker will suddenly stop serving the files in the cache.

The following is an example of how you can remove files from a cache that is not whitelisted (in this case, page-1 and Page-2 are in their

self.addEventListener('activate', function(event) { var cacheWhitelist = ['page-1', 'page-2']; event.waitUntil( // Retrieving all the keys from the cache. caches.keys().then(function(cacheNames) { return Promise.all( // Looping through all the cached files. cacheNames.map(function(cacheName) { // If the file in the cache is not in the whitelist // it should be deleted. if (cacheWhitelist.indexOf(cacheName) === -1) { return caches.delete(cacheName); }})); })); });Copy the code

HTTPS demand

When you are building your Web application, you can use the Service Worker at localhost, but once you deploy it to production, you must have HTTPS ready (and this is the last reason you use HTTPS).

With the Service Worker, you can hijack the connection and make the response. If you don’t use HTTPS, your Web application is vulnerable to man-in-the-middle attacks.

For security reasons, you need to register the Service Worker on the Service that uses HTTPS to know that the Service Worker request received by the browser has not been modified during network transmission.

Browser support

Browser support for Service workers is also getting better:

You can be in the process of this reference all browsers – jakearchibald. Making. IO/isservicewo… .

Service Workers embrace better features

The Service Worker provides some very specific features:

service

  • Push notifications – allows users to opt for timely updates from web applications.

  • Background synchronization – allows operations to be performed until the user has a stable connection. This way you can be sure that whatever the user wishes to send will be sent.

  • Periodic Synchronization (future) – THE API provides later periodic synchronization management capabilities.

  • Geofencing (future) – You can define parameters or think of geofencing as areas of interest. The Web app sends push notifications when a device passes through an electronic fence, allowing you to provide a meaningful experience based on the user’s geographic information.

We’ll continue to discuss these features in detail in subsequent blogs in this series.

So far we’ve been making SessionStack UX smoother, optimizing page loading and response times.

If you replay a user session in the session Stack (or observe it in real time), the session Stack’s foreground will continuously fetch data from the server, seamlessly creating a cache-like user experience for you. To give you some background – once you integrate the SessionStack library into your Web application, it will continue to collect data such as DOM changes, user interactions, network requests, unhandled exceptions, and debug messages.

When a session is recreated or streamed live, session Stack will provide problems with all the data, allowing you to see all of your users’ experiences in the browser (both visually and technically). This process must be fast enough, because we don’t want users to wait.

Since the data is extracted by our front end, this is a good place to leverage the Service Worker to handle situations like reloading our player and streaming again. Dealing with slow Internet connections is also important.

This is a free plan and you can try SessionStack.

resources