Visualization of the work of service workers



Good day, friends!

Many of you have probably heard of such an innovation in the JavaScript ecosystem as Service Workers, which are a key element of modern web development. Service workers are becoming increasingly popular, primarily due to the popularity of Progressive Web Applications (PWA).

When I first heard about them, I asked myself: “When should we use service workers? In what scenarios or context can we use them? ”

In this article, we will look at several practical examples of using service workers, which later, I dare to hope, will make your users happy.

I believe, before analyzing practical examples, it is necessary to at least outline the theoretical foundations of the work of service workers. For beginners, this will be a good help in future endeavors.

What is a service worker?


Cersis Worker is a script run by a browser in the background process. Remember that a service worker is completely independent of the page with which it interacts or which it serves (to serve).

In essence, a service worker is a proxy server between a web application, a browser, and a network.

Service workers allow web applications to work like native applications.

Some facts about service workers


  • Service workers do not have direct access to the DOM. To do this, they use the mechanism for responding to requests through the postMessages interface.
  • Service workers are forcibly canceled (stopped) when not in use. This means that they are event driven.
  • Service workers assume the use of promises (promises).
  • Due to the great capabilities, service workers can only be used via HTTPS. On a local server, you can do without HTTPS.

How do service workers work? Quick look


Service workers allow you to intercept server requests and cache these requests in order to improve application performance. Thus, productivity gains are achieved through caching of all content.

But it’s better to see it once, so here is an image showing the work of a service worker:



Service Worker Life Cycle


As I mentioned earlier, service workers work independently of the control page. If you want to install a service worker in your application, the first thing you need to do is register it.

After that, the browser that launched the installation of the service worker goes into the background:



Common Use Cases


Now that we know how service workers work, it's time to talk about where they are used.

Caching


As noted above, service workers can be used for caching. Here are some examples:

  • Only caching - you have static content that never changes.
  • Network or cache - you want to show users relevant content with a quick download condition.
  • Cache and update - you want to display content instantly and do not mind periodic synchronization with the server.
  • Cache, update and reboot - you want to show content as quickly as possible, implicitly updating its individual parts and displaying them in some "seamless" way.

Web Push


Web push allows applications to send push notifications and display content received in response to these notifications.

  • Push and update content - you want to share (deliver and receive) available content.
  • Push and content - you want to operate not only with text, but also with other types of information that enrich your messages.
  • Saturated push - you want to display images, the boot process and other things that improve the message you want to deliver.
  • Push and client - you want to show notifications depending on the state of the application.

More complex use cases


Analytics API


I have an application. And I want to add to it the ability to monitor the use of the application. To do this, I take a synchronous API to update the collected data from time to time.

Load balancer


Suppose you want to be able to choose the best content provider based on server performance. In this case, you need a service worker to intercept requests and make a selection.

I highly recommend you visit ServiceWorke.rs for a more in-depth look at service workers.

We practice skills


As I always say: "If you want to learn how to swim, get into the water." Learning a theory is a wonderful thing, but until you get your hands dirty, you won't learn anything.

Service Worker Registration


If we again turn to the illustration of the life cycle of a service worker, we will see that first of all we need to install it. To do this we need to register it.

//   
if('serviceWorker' in navigator){
    console.log('- ')
    //     
    //      "load"
    window.addEventListener('load', () => {
        //  -
        navigator.serviceWorker
        .register('/service-worker.js')
        .then(registration => {
            //   
            console.log(`-  , scope: ${registration.scope}`) // scope -  ,     -
        })
    })
    .catch(error => {
        //  
        console.log(`    : ${error}`)
    })
}

You can verify the work of the service worker by going to: Chrome: // inspect / # service-workers.



Also, information on the status of the service worker can be obtained in the developer tools: Application -> Service Workers.



What's next?


Now we need to cache all the files. We can select files for caching. Here's what it looks like:

//  
const CACHE_NAME = 'example.com-v1'
//    
const cacheAssets = ['index.html', 'about.html', 'js/main.js']
//    "install"
self.addEventListener('install', e => {
    console.log('- ')
    e.waitUntil(
        caches
            .open(CACHE_NAME)
            .then(cache => {
                console.log('  -:  ')
                cache.addAll(cacheAssets)
            })
            .then(() => {
                self.skipWaiting()
            })
    )
})

Here's what happens here:

  1. We determine the name of the cache (example.com-v1).
  2. We select files for caching. To do this, create an array.
  3. Inside the “install” event handler, we tell the browser to wait for the completion of the promise, then open the cache, which will be saved under the name “example.com-v1”.
  4. Finally, add the selected files to the cache.

Delete unused cache


Next, we need to remove the old cache versions:

//    "activate"
self.addEventListener('activate', e => {
    console.log('- ')
    e.waitUntil(
        caches.keys().then(cacheNames => {
            return Promise.all(
                cacheNames.map(cache => {
                    if(cache !== CACHE_NAME){
                        console.log('   ')
                        return caches.delete(cache)
                    }
                })
            )
        })
    )
})

Receive response


None of the above makes sense if we have no way to get cached content.

It can be obtained using the fetch event handler:

//    "fetch"
self.addEventListener('fetch', e => {
    e.respondWith(
        fetch(e.request)
            .then(res => {
                const copyCache = res.clone()
                caches.open(CACHE_NAME).then(cache => {
                    cache.put(e.request, copyCache)
                })
                return res
            })
            .catch(error => caches.match(e.request).then(res => res))
    )
})

All code can be viewed here .

Thank you for attention.

All Articles