Skip to content Skip to sidebar Skip to footer

Can A Service Worker Fetch And Cache Cross-origin Assets?

I'm using some service worker code from the Progressive Web app tutorial by Google but I am getting an error: Uncaught (in promise) TypeError: Failed to execute 'clone' on 'Resp

Solution 1:

You're on the right track with using clone(), but the timing is important. You need to make sure that you call clone() before the final return response executes, because at that point, the response will be passed to the service worker's client page, and its body will be "consumed".

There are two ways of fixing this: either call clone() prior to executing the asynchronous caching code, or alternatively, delay your return response statement until after the caching has completed.

I'm going to suggest the first approach, since it means you'll end up getting the response to the page as soon as possible. I'm also going to suggest that you rewrite your code to use async/await, as it's much more readable (and supported by any browser that also supports service workers today).

addEventListener("fetch", function(e) {
  e.respondWith((asyncfunction() {
    const cachedResponse = await caches.match(e.request);
    if (cachedResponse) {
      return cachedResponse;
    }

    const networkResponse = awaitfetch(e.request);

    const hosts = [
      'https://fonts.googleapis.com',
      'https://maxcdn.bootstrapcdn.com',
      'https://cdnjs.cloudflare.com',
    ];

    if (hosts.some((host) => e.request.url.startsWith(host))) {
      // This clone() happens before `return networkResponse` const clonedResponse = networkResponse.clone();

      e.waitUntil((asyncfunction() {
        const cache = await caches.open(CACHE_NAME);
        // This will be called after `return networkResponse`// so make sure you already have the clone!await cache.put(e.request, clonedResponse);
      })());
    }

    return networkResponse;
  })());
});

Note: The (async function() {})() syntax might look a little weird, but it's a shortcut to use async/await inside an immediately executing function that will return a promise. See http://2ality.com/2016/10/async-function-tips.html#immediately-invoked-async-function-expressions

For the original code, you need to clone the response before you do the asynchronous cache update:

var clonedResponse = response.clone();
        caches.open(CACHE_NAME).then(function(cache) {
          cache.put(e.request, clonedResponse);
        });

The Service Worker primer by Google has example code showing the correct way. The code has a comment with an "important" note, but it's just emphasizing the clone, and not the issue you're having about when you clone:

// IMPORTANT: Clone the response. A response is a stream// and because we want the browser to consume the response// as well as the cache consuming the response, we need// to clone it so we have two streams.var responseToCache = response.clone();

        caches.open(CACHE_NAME)
          .then(function(cache) {
            cache.put(event.request, responseToCache);
          });

        return response;

Post a Comment for "Can A Service Worker Fetch And Cache Cross-origin Assets?"