In the last blog, we talk about how to register and activate a Worker. In this post, we will discuss how to implement a cache strategy, and as there are so many of them. We will be looking at the basic Network and Cache.
"use strict";
const version = 11;
var isOnline = true;
var isLoggedIn = false;
var cacheName = `ramblings-${version}`;var urlsToCache = {
loggedOut: [
"/",
"/about",
"/contact",
"/404",
"/login",
"/offline",
"/css/style.css",
"/js/blog.js",
"/js/home.js",
"/js/login.js",
"/js/add-post.js",
"/js/external/idb-keyval-iife.min.js",
"/images/logo.gif",
"/images/offline.png",
],
};self.addEventListener("fetch", onFetch);function onFetch(e) {
e.respondWith(router(e.request));
}async function router(req) {
var url = new URL(req.url);
var res;
var reqURL = url.pathname;
var cache = await caches.open(cacheName);// request for site's own URL?
if (url.origin == location.origin) {
try {
let fetchOptions = {
method: req.method,
headers: req.headers,
credentials: "omit",
cache: "no-store",
};// try to get from the server
let res = await fetch(req.url, fetchOptions);
if (res && res.ok) {
// then cache it
await cache.put(reqURL, res.clone());
return res;
}
} catch (err) {
// if we can't get from server, let's see if it's in cache
res = await cache.match(reqURL);
if (res) {
return res;
}
}
}
}
You can see we are using afetch()
method. It starts the process of fetching a resource from the network, returning a promise which is fulfilled once the response is available. The promise resolves to the Response
object representing the response to your request. The promise does not reject on HTTP errors — it only rejects on network errors. You must use then
handlers to check for HTTP errors.
Here we return the default network request with return fetch(event.request)
, which returns a promise. We can use respondWith()
to answer ASAP.
When this promise is resolved, we respond by running a function that grabs our cache using caches.open(cacheName)
; this also returns a promise. When that promise resolves, cache.put()
is used to add the resource to the cache. The resource is grabbed from event.request
, and the response is then cloned with response.clone()
and added to the cache. The clone is put in the cache, and the original response is returned to the browser to be given to the page that called it.
Cloning the response is necessary because request and response streams can only be read once. In order to return the response to the browser and put it in the cache we have to clone it. So the original gets returned to the browser and the clone gets sent to the cache. They are each read once.
Now we have cached the asset, but we normally want to clean the old cache in the activation stage.
self.addEventListener("activate", onActivate);async function onActivate(e) {
// tell not to shut down until handleActivation is finished
e.waitUntil(handleActivation());
}async function handleActivation() {
//clean old cache
await clearCaches();
await clients.claim();
console.log(`service worker ${version} is activated ...`);
}async function clearCaches() {
var cacheNames = await caches.keys();
var oldCacheNames = cacheNames.filter(function matchOldCache(cacheName) {
if (/^ramblings-(\d+)$/.test(cacheName)) {
/*only delete cache created by us */
let [, cacheVersion] = cacheName.match(/^ramblings-(\d+)$/);
cacheVersion = cacheVersion != null ? Number(cacheVersion) : cacheVersion;
return cacheVersion > 0 && version != cacheVersion;
}
});
await Promise.all(
oldCacheNames.map(function deleteCache(cacheName) {
return caches.delete(cacheName);
})
);
}
Here we have a handleActivation
Promises passed into waitUntil()
in activate ()
that will block other events until completion, so you can rest assured that your clean-up operation will have completed by the time you get your first fetch
event on the new service worker.
In the deletion process, we first get all the cache names by calling the .keys()
and then test them against our url pattern to see if it’s our cache (there can be cache created by other sources). Then we get all the cache versions that match the cache name pattern. Finally we will iterate through them and delete them.
That’s so much of it.
Happy Reading!