Reputation:
There are numerous examples out there for initializing a service worker with a single cache similar to the following:
let cacheName = 'myCacheName-v1';
let urlsToCache = ['url1', 'url2', url3'];
self.addEventListener('install', function (event) {
event.waitUntil(
caches.open(cacheName).then(function (cache) {
return cache.addAll(urlsToCache);
}).then(function () {
return this.skipWaiting();
})
);
});
I wish to initialize multiple caches on my service worker. The motivation is to group assets by their tendency for change (e.g., static app data vs. css, javascript, etc.). With multiple caches, I can update individual caches (via versioned cache names) as files within that cache change. Ideally, I wish to setup a structure similar to the following:
let appCaches = [{
name: 'core-00001',
urls: [
'./',
'./index.html', etc...
]
},
{
name: 'data-00001',
urls: [
'./data1.json',
'./data2.json', etc...
]
},
etc...
];
My best attempt so far is something similar to:
self.addEventListener('install', function (event) {
appCaches.forEach(function (appCache) {
event.waitUntil(
caches.open(appCache.name).then(function (cache) {
return cache.addAll(appCache.urls);
}));
});
self.skipWaiting();
});
This approach seems to work. However, I'm still a newbie to service workers and promises. Something tells me that this approach has a pitfall that I'm too inexperienced to recognize. Is there a better way to implement this?
Upvotes: 6
Views: 2967
Reputation:
Thanks pirxpilot! Your answer, combined with JavaScript Promises: an Introduction, and a lot of trial and error figuring out how to chain all these Promises together resulted in a nice little implementation.
I added a requirement to only cache changes. Per best practices, the old cache is deleted during the 'activate' event. The end product is:
let cacheNames = appCaches.map((cache) => cache.name);
self.addEventListener('install', function (event) {
event.waitUntil(caches.keys().then(function (keys) {
return Promise.all(appCaches.map(function (appCache) {
if (keys.indexOf(appCache.name) === -1) {
return caches.open(appCache.name).then(function (cache) {
console.log(`caching ${appCache.name}`);
return cache.addAll(appCache.urls);
})
} else {
console.log(`found ${appCache.name}`);
return Promise.resolve(true);
}
})).then(function () {
return this.skipWaiting();
});
}));
});
self.addEventListener('activate', function (event) {
event.waitUntil(
caches.keys().then(function (keys) {
return Promise.all(keys.map(function (key) {
if (cacheNames.indexOf(key) === -1) {
console.log(`deleting ${key}`);
return caches.delete(key);
}
}));
})
);
});
Upvotes: 8
Reputation: 1647
It's better to call event.waitUntil
only once in a handler, but the good thing is it's relatively easy to get a single Promise to wait for.
Something like that should work:
event.waitUntil(Promise.all(
myCaches.map(function (myCache) {
return caches.open(myCache.name).then(function (cache) {
return cache.addAll(myCache.urls);
})
)
));
Promise.all
takes an Array of Promises and resolves only after all the promises in the Array resolve, which means that install
handler will wait till all the caches are initialized.
Upvotes: 9