📜 ⬆️ ⬇️

How JS Works: Service Workers


Here is a translation of the eighth part of a series of materials about the features of the work of various JavaScript mechanisms. Today’s article is about service workers. Here we look at their features, talk about their life cycle, their support in browsers, and scenarios for their use.



Progressive web applications


Progressive web applications are likely to become more and more popular. They are aimed at ensuring that the user perceives them not as regular web pages, but as something like classic desktop applications that work normally, regardless of whether the computer is connected to the Internet or not.

From here comes one of the basic requirements for progressive web applications, which is to ensure their reliable operation when there is no or unstable network connection. Service workers are an important technical part of implementing similar application behavior.

Application, service worker, cache and network resources
')
Here you can see a simplified diagram of the relationship between an application, a service worker, a cache managed by a service worker, and network resources. Ideally, proper organization of the application interaction with the service worker and cache will allow the user to work normally with the application even without connecting to the network.

Features of service workers


If you want to learn how service workers should study, you should look at the previous material in this series, which is dedicated to web workers. In general, it can be said that service workers are a type of web workers, or more precisely, they are similar to shared workers . In particular, the following important features of service workers can be identified:


ServiceWorkers API deserves special attention for the reason that it allows applications to support offline work scenarios, giving the programmer complete control over how the application interacts with external resources.

Service Worker Life Cycle


The life cycle of a service worker has nothing to do with the life cycle of a web page. It includes the following steps:


â–ŤDownload


At this stage of the life cycle, the web browser loads a .js file containing the code of the service worker.

â–ŤInstallation


In order to install a service worker, you first need to register it. This is done in javascript code. When the service worker is registered, the browser is prompted to run the installation in the background.

By registering a service worker, you tell the web browser where its .js file is located. Take a look at the following code:

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

It checks whether the Service Worker API is supported in the current environment. If this API is supported, then the service worker /sw.js .

The register() method can be easily called every time the page loads. The browser will independently find out if the corresponding service worker has already been registered and will correctly process the repeated request for registration.

An important feature in working with the register() method is the location of the service worker file. In this case, you can see that the service worker file is located in the root of the domain. As a result, the service domain will be the entire domain. In other words, this service worker will receive fetch events (which we will discuss below) generated by all pages from this domain. Similarly, if you register a service worker file located at /example/sw.js , this service worker will only see fetch events from pages whose URL starts with /example/ (i.e., for example, /example/page1/ , /example/page2/ ).

During the installation of the service worker, it is recommended to load and cache static resources. After caching, the installation of the web worker will be successfully completed. If the download fails, it will automatically attempt to reinstall. As a result, after successful installation of the web worker, the developer can be sure that all the necessary static materials are in the cache.

All this illustrates the fact that it is impossible to say that it is absolutely necessary that the registration of the web worker takes place after the load event, but it is recommended to do this.

Why? Imagine that a user first opens a web application. At this moment, the service worker for this application has not yet been loaded; moreover, the browser cannot find out in advance whether the application will use the service worker. If the service worker is installed, the browser will need to spend additional system resources. These resources would otherwise go on rendering the web page. As a result, the launch of the service worker installation process may delay the loading and output of the page. Usually, the developers strive to show the working page of the application to the user as soon as possible, but in our case the application cannot work normally without a service worker.

Please note that the above reasoning makes sense only when talking about the first visit to the page. If, after installing the service worker, the user visits the same page again, re-installation will not be performed, which means that the speed of the working page will not be affected. After the first visit to the application page, the service worker will be activated, as a result, it will be able to handle loading and caching events on subsequent visits to the web application. As a result, the application will be ready to work in a limited network connection.

â–ŤActivation


After installing the service worker, we proceed to the next stage of its life cycle - activation. At this step, the developer has the opportunity to work with the data that was cached earlier.

After activation, the service worker will be able to manage all the pages that fall within his scope. It is worth noting that the service worker’s mechanisms will not act on the page that registered it until this page is reloaded.

After the service worker receives control, he may be in one of the following states:


Here is the life cycle of the service worker:

Service Worker Life Cycle

Processing the installation process inside the service worker


A service worker, after the registration process was launched, is able to influence what is happening. In particular, we are talking about the install event handler in the service worker code.

Here's what the service worker needs to do when handling the install event:


Here is a simple example of handling an install event in 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    ,  , //    ,   //    . event.waitUntil(   caches.open(CACHE_NAME)     .then(function(cache) {       console.log('Opened cache');       return cache.addAll(urlsToCache);     }) ); }); 

If all materials are successfully cached, this means a successful installation of the service worker. If something fails to load, then the installation will be declared invalid. Therefore, you should pay special attention to what data you want to put in the cache.

It should be noted here that handling the install event inside the service worker is optional.

Working with the cache during the execution of the application


This is where the fun begins. This is where we will look at the mechanisms for intercepting requests, returning cached data and caching new materials.

After the service worker is installed and the user moves to another page of the application or refreshes the page on which it is located, the service worker will start receiving fetch events. Here is an example that shows how to return cached content or execute new queries, and then cache the result:

 self.addEventListener('fetch', function(event) { event.respondWith(   //        //            //  - .   caches.match(event.request)     .then(function(response) {       //     ,  ,      .       if (response) {         return response;       }       //  .     -  ,       //       .       //          ,       //      ,   ,       //     .       var fetchRequest = event.request.clone();             //     ,     ,       //          ,        // ,  ,     .       return fetch(fetchRequest).then(         function(response) {           //  ,                if(!response || response.status !== 200 || response.type !== 'basic') {             return response;           }           //   ,      .           //    ,     ,           //    ,   ,           //        .           var responseToCache = response.clone();           caches.open(CACHE_NAME)             .then(function(cache) {               //       .               cache.put(event.request, responseToCache);             });           return response;         }       );     })   ); }); 

Here is what happens in general:


Request and response objects must be cloned, as they are streams . The stream can be processed only once. However, both the service worker and the browser need to work with these threads.

Service Worker Update


When a user visits a web application, the browser tries to reload the .js file that contains the code of the service worker. This process is performed in the background.

If there is even the smallest difference between the service worker file that was downloaded and the current file, the browser will decide that there are changes in the worker code. This means that the application should use a new service worker.

The browser will start this new service worker and raise the install event. However, at this moment, the old worker is still responsible for the interaction of the application with the outside world. Therefore, the new service worker will be in a state of waiting.

After the current open page of the web application is closed, the old service worker will be stopped by the browser, and the newly installed service worker will have full control over what is happening. At this point, its activate event will be activate .

Why is all this necessary? In order to avoid the problem of having two versions of a web application running simultaneously in different browser tabs. This, in fact, is quite common, and the situation in which different tabs are controlled by different web workers can cause serious errors (for example, using different data schemes for local information storage).

Deleting cache data


When processing the activate event of a new version of the service worker, they usually work with the cache. In particular, old caches are deleted here. If you do this earlier, during the installation of a new worker, the old service worker will not be able to work normally.

Here is an example of how you can delete files from the cache that were not placed in the white list (in this case, the white list is represented by page-1 and page-2 entries):

 self.addEventListener('activate', function(event) { var cacheWhitelist = ['page-1', 'page-2']; event.waitUntil(   //     .   caches.keys().then(function(cacheNames) {     return Promise.all(       //     .       cacheNames.map(function(cacheName) {         //         ,         //   .         if (cacheWhitelist.indexOf(cacheName) === -1) {           return caches.delete(cacheName);         }       })     );   }) ); }); 

Using HTTPS


During the development of a web application, service workers will normally work on localhost , but after the release of the application in production you will need to use HTTPS (perhaps, if you are not using HTTPS yet, this is a very good reason to improve the situation).

A service worker who is not HTTPS protected is subject to intermediary attacks , since an attacker can intercept connections and create fake responses to application requests. That is why, in order to make the system safer, the developer must register service workers on the pages that are served via HTTPS. In particular, it gives confidence that the service worker loaded into the browser was not modified during the transmission of its code over the network.

Browser support


While the situation with the support of service workers browsers is not perfect, but it is improving:


Support for service workers in browsers

Here you can watch the process of introducing support for API service workers to browsers.

Usage scenarios


Service workers open the way for great web application features. Here are some of them:


Results


Service workers are a promising technology that is the basis for progressive web applications. This technology, as well as its individual capabilities, is not yet supported by all browsers, but we have every reason to expect an improvement in the situation. Therefore, it is possible that in the foreseeable future, service worker talents will be able to fully open up in all leading browsers, giving developers new tools and ideas.

Previous parts of a series of articles:

Part 1: How JS Works: Overview of the Engine, Runtime Mechanisms, Call Stack
Part 2: How JS Works: About V8 Inside and Code Optimization
Part 3: How JS works: memory management, four types of memory leaks and how to deal with them
Part 4: How JS works: event loop, asynchrony, and five ways to improve code with async / await
Part 5: How JS: WebSocket and HTTP / 2 + SSE work. What to choose?
Part 6: How JS Works: Features and Scope of WebAssembly
Part 7: How JS Works: Web Workers and Five Use Cases

Dear readers! Which scenarios for using service workers in your projects seem most interesting to you?

Source: https://habr.com/ru/post/349858/


All Articles