Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Service Workers not updating

Tags:

I have a service worker installed in my website, everything works fine, except when I push an update to the cached files, in fact; they stay catched forever and I seem to be unable to invalidate the cache unless I unsubscribe the worker from the `chrome://serviceworker-internals/

const STATIC_CACHE_NAME = 'static-cache-v1';
const APP_CACHE_NAME = 'app-cache-#VERSION';

const CACHE_APP = [
    '/',
    '/app/app.js'
]
const CACHE_STATIC = [
    'https://fonts.googleapis.com/css?family=Roboto:400,300,500,700',
    'https://cdnjs.cloudflare.com/ajax/libs/normalize/4.1.1/normalize.min.css'
]

self.addEventListener('install',function(e){
    e.waitUntil(
        Promise.all([caches.open(STATIC_CACHE_NAME),caches.open(APP_CACHE_NAME)]).then(function(storage){
            var static_cache = storage[0];
            var app_cache = storage[1];
            return Promise.all([static_cache.addAll(CACHE_STATIC),app_cache.addAll(CACHE_APP)]);
        })
    );
});

self.addEventListener('activate', function(e) {
    e.waitUntil(
        caches.keys().then(function(cacheNames) {
            return Promise.all(
                cacheNames.map(function(cacheName) {
                    if (cacheName !== APP_CACHE_NAME && cacheName !== STATIC_CACHE_NAME) {
                        console.log('deleting',cacheName);
                        return caches.delete(cacheName);
                    }
                })
            );
        })
    );
});

self.addEventListener('fetch',function(e){
    const url = new URL(e.request.url);
    if (url.hostname === 'static.mysite.co' || url.hostname === 'cdnjs.cloudflare.com' || url.hostname === 'fonts.googleapis.com'){
        e.respondWith(
            caches.match(e.request).then(function(response){
                if (response) {
                    return response;
                }
                var fetchRequest = e.request.clone();

                return fetch(fetchRequest).then(function(response) {
                    if (!response || response.status !== 200 || response.type !== 'basic') {
                        return response;
                    }
                    var responseToCache = response.clone();
                    caches.open(STATIC_CACHE_NAME).then(function(cache) {
                        cache.put(e.request, responseToCache);
                    });
                    return response;
                });
            })
        );
    } else if (CACHE_APP.indexOf(url.pathname) !== -1){
        e.respondWith(caches.match(e.request));
    }
});

where #VERSION is a version that is appended to the cache name at compile time; note that STATIC_CACHE_NAME never changes, as the files are thought to be static forever.

Also the behavior is erratic, I've been checking the delete function (the part where it logs) and it keeps logging about the deleting caches that have already been deleted (supposedly). when I run caches.keys().then(function(k){console.log(k)}) I get a whole bunch of old caches that should've been removed.

like image 853
Onza Avatar asked May 09 '16 14:05

Onza


People also ask

Are service workers cached?

A service worker intercepts network-type HTTP requests and uses a caching strategy to determine what resources should be returned to the browser.

Do service workers work when browser is closed?

This means the browser can have no windows open, and you'll still receive the push message in your service worker, because the browser in running in the background. The only time a push won't be received is when the browser is completely closed, i.e. not running at all (no marking).


2 Answers

After googling and watching some videos on udacity, I found that the intended behavior of the worker is to stay until the page it controls is closed and reopened again when the new service worker can take control.

The solution was to force it to take control based on https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/skipWaiting the following solved the issue, even when it takes 2 reloads in order for the new worker to reflect the changes (that makes sense since the app is loaded before the new worker replaces the previous).

self.addEventListener('install',function(e){
    e.waitUntil(
        Promise.all([caches.open(STATIC_CACHE_NAME),caches.open(APP_CACHE_NAME),self.skipWaiting()]).then(function(storage){
            var static_cache = storage[0];
            var app_cache = storage[1];
            return Promise.all([static_cache.addAll(CACHE_STATIC),app_cache.addAll(CACHE_APP)]);
        })
    );
});

self.addEventListener('activate', function(e) {
    e.waitUntil(
        Promise.all([
            self.clients.claim(),
            caches.keys().then(function(cacheNames) {
                return Promise.all(
                    cacheNames.map(function(cacheName) {
                        if (cacheName !== APP_CACHE_NAME && cacheName !== STATIC_CACHE_NAME) {
                            console.log('deleting',cacheName);
                            return caches.delete(cacheName);
                        }
                    })
                );
            })
        ])
    );
});
like image 156
Onza Avatar answered Sep 19 '22 12:09

Onza


As a "service worker newbie" I encountered a related situation whereby a service worker wouldn't refresh even though the "JavaScript Console>Application>Update on reload" was enabled on Chrome Canary.

The problem was that I had working code in my /sw/index.js and then I introduced an error to /sw/index.js. When I introduced the error the browser refused to load the updated code and continued to load the earlier working service worker. When I corrected the code in index.js and refreshed the page the new code for the service worker appeared. I would have thought that the error filled code would throw an error, but it didn't. The browser just loaded the earlier error free version.

like image 34
Bruce Seymour Avatar answered Sep 18 '22 12:09

Bruce Seymour