-
-
Save adactio/3717b7da007a9363ddf21f584aae34af to your computer and use it in GitHub Desktop.
// Licensed under a CC0 1.0 Universal (CC0 1.0) Public Domain Dedication | |
// http://creativecommons.org/publicdomain/zero/1.0/ | |
// HTML files: try the network first, then the cache. | |
// Other files: try the cache first, then the network. | |
// Both: cache a fresh version if possible. | |
// (beware: the cache will grow and grow; there's no cleanup) | |
const cacheName = 'files'; | |
addEventListener('fetch', fetchEvent => { | |
const request = fetchEvent.request; | |
if (request.method !== 'GET') { | |
return; | |
} | |
fetchEvent.respondWith(async function() { | |
const fetchPromise = fetch(request); | |
fetchEvent.waitUntil(async function() { | |
const responseFromFetch = await fetchPromise; | |
const responseCopy = responseFromFetch.clone(); | |
const myCache = await caches.open(cacheName); | |
return myCache.put(request, responseCopy); | |
}()); | |
if (request.headers.get('Accept').includes('text/html')) { | |
try { | |
return await fetchPromise; | |
} | |
catch(error) { | |
return caches.match(request); | |
} | |
} else { | |
const responseFromCache = await caches.match(request); | |
return responseFromCache || fetchPromise; | |
} | |
}()); | |
}); |
Got it!
And for bonus points, that last line can be simplified to return cachedResponse || networkResponsePromise;
.
Thanks you for this. I have the caching working and used the offline fallback example from the 2018.ampersand site.
Just wondered if you or @jakearchibald have a strategy for dealing with the requests that Google Analytics makes to https://www.google-analytics.com/collect and a way to stop them from being added to the cache?
@paulyabsley One option is to add in a catch-all that skips over any requests for files that aren't from your own domain, similar to how we're ignoring any non-GET requests. Right before or after that bit, you could add something like:
const url = new URL(request.url);
if (url.origin !== location.origin) {
return;
}
Ah cool, thanks!
Just to note that the main gist here is missing an await
in the "return fetchPromise" line (as given in @jakearchibald's update), which means as-is it won't respond from the cache for HTML pages - as the promise can always be returned so will be, regardless of whether it resolves or rejects.
Here's a method what creates a promise that resolves after some milliseconds:
This function takes two seconds to complete:
The async function yields for a second when it hits the first
wait(1000)
, then it continues and yields for another second when it hits the secondwait(1000)
.Whereas this function takes one second to complete:
It gets a promise for a one second wait, but it doesn't
await
it, instead it goes straight to the next line,await wait(1000);
, where it does wait one second. Then, it awaits the completion ofwaitPromise
, but that started a second ago, so it's already done.Async functions are brilliant, but you need to ensure that you're still allowing things to happen in parallel, and avoid waiting on stuff you don't want to wait on.
The above isn't quite right, as the function yields on awaiting a network response before it can return the final value, so even the cache-first approach is blocked on the network.
The above example doesn't have the same problem.