Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Service worker sends two requests

Tags:

javascript

pwa

I've implemented a service worker which caches all requests for offline usage, this works fine. But everytime I load a page there are two requests hitting my webserver (one from the service worker and one from the browser)!

How can I cache the request and only load the page once?

service-worker.js

self.addEventListener('install', function(event) {
  //load error page which will show if user has no internet
  var errorPage = new Request('/?p=error&offline');
  event.waitUntil(pushToCache(errorPage));
});

//If any fetch fails, it will look for the request in the cache and serve it from there first
self.addEventListener('fetch', function(event) {
  event.waitUntil(pushToCache(event.request));

  event.respondWith(
    fetch(event.request) //try loading from internet
    .catch(function (error) {
      return fetchFromCache(event.request);
    }) //no internet connection try getting it from cache
  );
});

function pushToCache(request){
  if(request.method == "GET"){
    return caches.open('stm-app').then(function (cache) {
      return fetch(request).then(function (response) {
        return cache.put(request, response);
      });
    });
  }
};

function fetchFromCache(request) {
  return caches.open('stm-app').then(function (cache) {
    return cache.match(request).then(function (matching) {
      if(!matching || matching.status == 404){
        return fetchFromCache(new Request('/?p=error&offline')); //show page that user is offline
      }else{
        return matching;
      }
    });
  });
}

sw-register.js

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('service-worker.js')
  .then(function(registration) {
    console.log('Registered:', registration);
  })
  .catch(function(error) {
    console.log('Registration failed: ', error);
  });
}
like image 753
Aaron Stein Avatar asked Jul 09 '18 10:07

Aaron Stein


1 Answers

So here's what happens whenever you make a request:

  1. The webpage sends a fetch request to the server,
  2. the Service Worker intercepts the request on the 'fetch' event,
  3. pushToCache() fires a fetch request to the server in order to cache the response,
  4. then you respond to the event with a fetch request to the server which will return a Promise for a Response from the web server.

Yup, that makes sense, that thing just sent two requests two the server for every request the page originally made.

One thing you might want to consider is responding from the cache first and then going on the network to get the latest data. This way you will avoid delays in loading in the case of connection issues and it will speed up the loading time of the page even when the user is online.

Let's consider the following scenario: Either the user or the server are offline. Once you fire the request, it will have to time out before it goes to the catch part of the promise and get the cached response.

What you could do once you intercept the event is check the caches for a match and if you find anything, respond to the event with that. Then start a fetch request in order to update the cache. Now if you don't find anything, fire a fetch request, clone the response (because the response body can only be used once), respond with the original response and then update the cache with the cloned response.

What did we achieve with that?

The user gets an instant response, no matter if he's online, offline or on Lie-Fi!

The server gets at most one request and the caches will always get updated with the latest data from the server!

serviceworke.rs is a great resource that can help you understand how to do many interesting things with Service Workers.

This page in particular explains in a bit more detail how what I said above works.

like image 165
Konstantinos Kamaropoulos Avatar answered Sep 20 '22 11:09

Konstantinos Kamaropoulos