Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ERR_FAILED when service worker loads new page, why?

I've written a service worker with help from a tutorial:

var CACHE = 'cache-and-update';

self.addEventListener('install', function (evt) {
    console.log('The service worker is being installed.');

    evt.waitUntil(precache());
});

self.addEventListener('fetch', function (evt) {

    evt.respondWith(fromCache(evt.request));

    evt.waitUntil(update(evt.request));
});

function precache() {
    return caches.open(CACHE).then(function (cache) {
        return cache.addAll([
            // Nothing.
        ]);
    });
}

function fromCache(request) {
    return caches.open(CACHE).then(function (cache) {
        return cache.match(request).then(function (matching) {
            return matching || Promise.reject('no-match');
        });
    });
}

function update(request) {
    return caches.open(CACHE).then(function (cache) {
        return fetch(request).then(function (response) {
            return cache.put(request, response);
        });
    });
}

It always serves from the cache first, then fetches all files, and updates on page reload.

The service worker is registered like this in every HTML file on my server:

<script>
    navigator.serviceWorker.register('https://www.example.com/sw.js', {
        scope: '../'
    });
</script>

Now the problem is, when I go to a page that isn't cached yet, it first shows me the default Chrome ERR_FAILED error (and the 'no-match' promise rejection).

The sw then fetches it anyway, while showing the error page to the client, and on a reload it works again (because it's served from the cache).

Why is this happening and how can I make the service worker load the page from the server when there's no cached version available?

like image 498
Sacha Avatar asked Aug 19 '18 15:08

Sacha


1 Answers

You got the fetch listener wrong here, You are rejecting the promise if the file was not found in the cache, you should fetch it then cache it instead of returning Promise.reject('no-match') and you do not need the evt.waitUntil in this case

here is a full snippet to a working service worker. If a request doesn't match anything in the cache, we get it from the network, send it to the page and add it to the cache at the same time.

let cacheName = 'cache-v1';

self.addEventListener('install', (e) => {

  let cache = caches.open(cacheName).then((c) => {
    c.addAll([
      // nothing
    ]);
  });

  e.waitUntil(cache);
});

self.addEventListener('fetch', function (event) {

  event.respondWith(

    caches.open(cacheName).then(function (cache) {
      return cache.match(event.request).then(function (response) {
        return response || fetch(event.request).then(function (response) {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    })

  );

});
like image 86
Hyyan Abo Fakher Avatar answered Sep 22 '22 09:09

Hyyan Abo Fakher