29

I'm trying to integrate service workers into my app, but I've found the service worker tries to retrieve cached content even when online, but I want it to prefer the network in these situations. How can I do this? Below is the code I have now, but I don't believe it is working. SW Install code is omitted for brevity.

var CACHE_NAME = 'my-cache-v1';
var urlsToCache = [
  /* my cached file list */
];

self.addEventListener('install', function(event) {
  // Perform install steps
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
});

/* request is being made */
self.addEventListener('fetch', function(event) {
  event.respondWith(
    //first try to run the request normally
    fetch(event.request).catch(function() {
      //catch errors by attempting to match in cache
      return caches.match(event.request).then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }
      });
    })
  );
});

This seems to lead to warnings like The FetchEvent for "[url]" resulted in a network error response: an object that was not a Response was passed to respondWith(). I'm new to service workers, so apologies for any mistaken terminology or bad practices, would welcome any tips. Thank you!

5
  • 1
    Do the warnings only occur while offline? Its possible you are getting requests for unexpected URLs, like favicons, that are not stored in your cache install event.
    – Ben Kelly
    Commented Oct 12, 2015 at 15:35
  • This happened when online, when offline the service worker actually seems to work as expected. Commented Oct 12, 2015 at 18:28
  • EDIT: I also get these errors when offline, but you're right, only for resources I don't have stored in cache. Commented Oct 12, 2015 at 18:37
  • @RubenMartinezJr. did you manage to figure this out? I'm getting the same issue on my Rails setup. Very weird. Commented May 17, 2016 at 17:20
  • I didn't really :/ I switched to using Google's SW-Toolbox instead, it's a much nicer API! Commented May 17, 2016 at 21:44

3 Answers 3

17

Without testing this out, my guess is that you're not resolving respondWith() correctly in the case where there is no cache match. According to MDN, the code passed to respondWith() is supposed to "resolve by returning a Response or network error to Fetch." So why not try just doing this:

self.addEventListener('fetch', function(event) {
  event.respondWith(
    fetch(event.request).catch(function() {
      return caches.match(event.request);
    })
  );
});
6
  • 4
    Also see Jake's offline-cookbook: jakearchibald.com/2014/offline-cookbook/…
    – Ben Kelly
    Commented Oct 12, 2015 at 15:31
  • 2
    This code is essentially the same as what I have above, and unfortunately results in the same errors :/ Commented Oct 12, 2015 at 18:35
  • 2
    Given that this seems to be the documented method, I'm going to accept this and assume there's something fishy going on somewhere...Will update if I discover more. Commented Oct 12, 2015 at 21:58
  • Just a thought, but are your sure the file name/url is correct? I'm testing something similar and it appears that the catch is not reached and then you get an undefined response object which causes this error.
    – Aries51
    Commented Oct 23, 2015 at 8:13
  • 1
    Another quick thought: Since you're getting the respondWith warning, it means the caches.match() isn't finding that resource. Could be as simple as a query string or trailing slash. Commented Jul 2, 2017 at 3:37
6

Why don't you open the cache for your fetch event? I think the process of a service worker is :

  • Open your cache

  • Check if the request match with an answer in your cache

  • Then you answer

OR (if the answer is not in the cache) :

  • Check the request via the network

  • Clone your answer from the network

  • Put the request and the clone of the answer in your cache for future use

I would write :

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.open(CACHE_NAME).then(cache => {
     return cache.match(event.request).then(response => {
      return response || fetch(event.request)
      .then(response => {
        const responseClone = response.clone();
        cache.put(event.request, responseClone);
        })
      })
    }
 );
});
5

event.respondWith() expects a promise that resolves to Response. So in case of a cache miss, you still need to return a Response, but above, you are returning nothing. I'd also try to use the cache first, then fetch, but in any case, as the last resort, you can always create a synthetic Response, for example something like this:

return new Response("Network error happened", {"status" : 408, "headers" : {"Content-Type" : "text/plain"}});

Not the answer you're looking for? Browse other questions tagged or ask your own question.