According to service worker documentation, the install event should only fire once per service worker; however, if I put a console.log inside of the install event listener, I am noticing that before a fetch occurs, the install event is always fired.
Why is the install event for the service worker getting called even when there have been no changes/updates to the SW. Does the service worker's install event get called if it goes idle for a short period of time? This is a problem as the caching is done during install, and if the install event gets called before the fetch occurs, we are updating the cache constantly when it doesn't need to be updated at all.
Thanks for the help!
const CACHE_NAME = 'my-cache-v2';
const cacheAllowList = [CACHE_NAME];
const externalFiles = [
various files here
];
self.addEventListener('fetch', function (event) {
event.respondWith(
self.caches
.match(event.request)
.then(function (response) {
if (response) {
return response;
}
return fetch(event.request);
})
);
});
self.addEventListener('install', function (event) {
// Perform install steps
event.waitUntil(
self.caches.keys().then(function (cacheNames) {
// Add cache
self.caches.open(CACHE_NAME).then((cache) => {
return cache.addAll([
...externalFiles
]);
})
.catch(error => {
console.log('Service worker cache failed: ' + error);
})
})
);
});
self.addEventListener('activate', function (event) {
// All caches not listed here will be deleted.
event.waitUntil(
self.caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames.map(function (cacheName) {
if (cacheAllowList.indexOf(cacheName) === -1) {
return self.caches.delete(cacheName);
}
})
);
})
);
});
self.addEventListener('push', function(event) {
console.log('[Service Worker] Push Received.');
console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);
const title = 'Push Codelab';
const options = {
body: 'Push notifications',
icon: 'images/icon.png',
badge: 'images/badge.png'
};
event.waitUntil(self.registration.showNotification(title, options));
});
*Edit - Its important to note that this doesn't always happen. Sometimes the fetch works fine without calling install, other times install gets called for seemly no reason before the fetch occurs.
I use a serviceworker to serve content offline. The following code works well except when I refresh the page in offline mode right after opening and installing the serviceworker in online-mode.
All these scenarios work:
1. Loading the page online, refreshing the page onlinge -> refreshing the page offline - works.
2. Loading "/offline-test/" online -> loading "/offline-test/index.html" offline - works
3. Loading "/offline-test/index.html/ online -> loading "/offline-test/" offline - works
Only this scenario isn't working:
Loading the page online -> refresh the page in offline-mode.
Why?
I'm using Chrome Dev tools to simulate Offline-Mode. Same problem in Firefox (turning WiFi off after loading the page).
index.html
<HTML>
...
<BODY>
...
</BODY>
<SCRIPT>
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('../sw.js')
.then(function() {
console.log("Service Worker Registered");
});
}
</SCRIPT>
</HTML>
sw.js
self.addEventListener('install', function(e) {
// self.skipWaiting();
if(navigator.onLine) {
console.log('Navigator is online');
e.waitUntil(caches.keys()
.then(function(keyList) {
console.log('Try to delete cache.');
return Promise.all(keyList.map(function(key) {
return caches.delete(key)
.then(function(response) {
console.log('Cache-Key '+key+' will be deleted');
return response;
}, function(reject) {
consoloe.log('Failure deleting '+key);
return reject;
});
}))
})
);
}
e.waitUntil(
caches.open('offline-test-v3').then(function(cache) {
console.log('index.html and all the others will be added to cache');
return cache.addAll([
'offline-test/',
'offline-test/index.html',
'offline-test/style.css',
'offline-test/logo-192.png',
'offline-test/fonts/ROCK.TTF',
'manifest.json',
'offline-test/favicon.ico'
])
.then(function(response) {
console.log('Everything succesfully added to cache');
});
})
);
});
self.addEventListener('activate', function(ev) {
// ev.waitUntil(self.clients.claim());
// clients.claim();
// ev.waitUntil(clients.claim());
console.log('I\'m activated');
});
self.addEventListener('update', function(eve) {
console.log('I\'m updated');
});
self.addEventListener('fetch', function(event) {
console.log('Will fetch '+event.request.url);
//Networ first
event.respondWith(
fetch(event.request).catch(function() {
console.log('Try to fetch '+event.request.url+' from cache');
return caches.match(event.request);
})
);
console after loading index.html online
Service Worker Registered
sw.js:9 Navigator is online
sw.js:12 Try to delete cache.
sw.js:29 index.html and all the others will be added to cache
sw.js:40 Everything succesfully added to cache
sw.js:58 I'm activated
console after refreshing in offline-mode
Will fetch index.html
sw.js:70 Try to fetch [url]/index.html from cache
The FetchEvent for [url]/index.html" resulted in a network error response: an object that was not a Response was passed to respondWith().
Solved this issue by adding the new cache as a resolve to the preceding cache-deleting-routine.
Probably the two threads conflicted.
New sw.js
self.addEventListener('install', function(e) {
if(navigator.onLine) {
console.log('Navigator is online');
e.waitUntil(caches.keys()
.then(function(keyList) {
console.log('Try to delete cache.');
return Promise.all(keyList.map(function(key) {
return caches.delete(key)
.then(function(response) {
console.log('Cache-Key '+key+' will be deleted');
return response;
}, function(reject) {
consoloe.log('Failure deleting '+key);
return reject;
});
}))
})
.then(function() {
caches.open('offline-test-v3').then(function(cache) {
console.log('index.html and all the others will be added to cache');
return cache.addAll([
'offline-test/',
'offline-test/index.html',
'offline-test/style.css',
'offline-test/logo-192.png',
'offline-test/fonts/ROCK.TTF',
'manifest.json',
'offline-test/favicon.ico'
])
.then(function(response) {
console.log('Everything succesfully added to cache');
});
})
}));
}
});
self.addEventListener('activate', function(ev) {
console.log('I\'m activated');
});
self.addEventListener('update', function(eve) {
console.log('I\'m updated');
});
self.addEventListener('fetch', function(event) {
console.log('Will fetch '+event.request.url);
//Network first
event.respondWith(
fetch(event.request)
.then(function(resolve) {
console.log('Fetched '+event.request.url+' via the browser way.');
return resolve;
}, function(reject) {
console.log('Try to fetch '+event.request.url+' via the serviceworker way');
return caches.match(event.request);
})
);
/* if(!navigator.onLine) {
console.log('Try to fetch '+event.request.url+' from cache.');
event.respondWith(caches.match(event.request)
.then(function(response) {
return response || fetch(event.request);
})
);
}*/
});
I am trying to set up pwa app. My sw.js is
var urlsToCache = [
'/danger/index.html',
'/danger/css/main.css',
'/danger/css/fontawesome-all.min.css',
'/danger/css/font.css'
];
var CACHE_NAME = 'progressive';
self.addEventListener('install', function (event) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function (cache) {
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.open(CACHE_NAME).then(function (cache) {
return cache.match(event.request).then(function (response) {
return response || fetch(event.request).then(function (response) {
cache.put(event.request, response.clone());
return response;
});
});
})
);
});
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.open(CACHE_NAME).then(function (cache) {
return fetch(event.request).then(function (response) {
cache.put(event.request, response.clone());
return response;
});
})
);
});
I want to make sw check for first network, then cache. I got a code from net.
self.addEventListener('fetch', (event) => {
event.respondWith(async function() {
try {
return await fetch(event.request);
} catch (err) {
return caches.match(event.request);
}
}());
});
How to implement it in my sw.js. I asked it because my app not updating the website content in network enabled mode also.
Browser will always reach out to network first and get the service-worker.js I would suggest you to use Workbox instead of manually writing all these code.
Workbox documentation
We should not cache service-worker file as that is a source of truth for browser to know whether any other files have changed. Usually a service-worker file will have a series of files that are cached along with their hashes. When service worker file changes, then browser knows that it needs to take update from server.
Thanks
I have the following service worker code in my Vue app:
main.js
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/service-worker.js').catch(function() {
console.log('Service worker registration failed.');
});
}
service-worker.js
let currCacheName = 'premium-poker-tools-5';
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(currCacheName).then(function(cache) {
let promise = cache.addAll([
'/',
'app.js',
'c7d016677eb7e912bc40.worker.js',
'f328c7e2b379df12fa4c.worker.js',
'static/logo.png',
'static/favicon.png',
'static/loading.svg',
'static/cards/ace-of-clubs.png',
'static/cards/king-of-clubs.png',
'static/cards/queen-of-clubs.png',
'static/cards/jack-of-clubs.png',
'static/cards/ten-of-clubs.png',
'static/cards/nine-of-clubs.png',
'static/cards/eight-of-clubs.png',
'static/cards/seven-of-clubs.png',
'static/cards/six-of-clubs.png',
'static/cards/five-of-clubs.png',
'static/cards/four-of-clubs.png',
'static/cards/three-of-clubs.png',
'static/cards/two-of-clubs.png',
'static/cards/ace-of-spades.png',
'static/cards/king-of-spades.png',
'static/cards/queen-of-spades.png',
'static/cards/jack-of-spades.png',
'static/cards/ten-of-spades.png',
'static/cards/nine-of-spades.png',
'static/cards/eight-of-spades.png',
'static/cards/seven-of-spades.png',
'static/cards/six-of-spades.png',
'static/cards/five-of-spades.png',
'static/cards/four-of-spades.png',
'static/cards/three-of-spades.png',
'static/cards/two-of-spades.png',
'static/cards/ace-of-hearts.png',
'static/cards/king-of-hearts.png',
'static/cards/queen-of-hearts.png',
'static/cards/jack-of-hearts.png',
'static/cards/ten-of-hearts.png',
'static/cards/nine-of-hearts.png',
'static/cards/eight-of-hearts.png',
'static/cards/seven-of-hearts.png',
'static/cards/six-of-hearts.png',
'static/cards/five-of-hearts.png',
'static/cards/four-of-hearts.png',
'static/cards/three-of-hearts.png',
'static/cards/two-of-hearts.png',
'static/cards/ace-of-diamonds.png',
'static/cards/king-of-diamonds.png',
'static/cards/queen-of-diamonds.png',
'static/cards/jack-of-diamonds.png',
'static/cards/ten-of-diamonds.png',
'static/cards/nine-of-diamonds.png',
'static/cards/eight-of-diamonds.png',
'static/cards/seven-of-diamonds.png',
'static/cards/six-of-diamonds.png',
'static/cards/five-of-diamonds.png',
'static/cards/four-of-diamonds.png',
'static/cards/three-of-diamonds.png',
'static/cards/two-of-diamonds.png',
'static/feedback/1.png',
'static/feedback/2.png',
'static/feedback/3.png',
'static/feedback/4.png',
'static/feedback/flop-selector.png',
'static/feedback/green-grid-squares.png',
'static/feedback/user-set-range-to-simulate-to-street.png',
'static/guides/beginners-guide/1.png',
'static/guides/beginners-guide/2.png',
'static/guides/beginners-guide/3.png',
'static/guides/beginners-guide/4.png',
'static/guides/beginners-guide/5.png',
'static/guides/beginners-guide/6.png',
'static/guides/beginners-guide/7.png',
'static/guides/beginners-guide/8.png',
'static/guides/beginners-guide/9.png',
'static/guides/beginners-guide/10.png',
'static/guides/beginners-guide/11.png',
'static/guides/beginners-guide/12.png',
'static/guides/beginners-guide/13.png',
'static/guides/beginners-guide/14.png',
'static/guides/beginners-guide/15.png',
'static/guides/beginners-guide/16.png',
'static/guides/beginners-guide/17.png',
'static/guides/beginners-guide/18.png',
'static/guides/beginners-guide/19.png',
'static/guides/beginners-guide/20.png',
'static/guides/beginners-guide/21.png',
'static/guides/faq/double-counting/1.png',
'static/guides/faq/hit-percentage-calculation/1.png',
'static/guides/faq/hit-percentage-calculation/2.png',
'static/guides/faq/hit-percentage-calculation/3.png',
'static/guides/faq/insights/1.png',
'static/guides/faq/insights/2.png',
'static/guides/faq/insights/3.png',
'static/guides/faq/insights/4.png',
'static/guides/faq/insights/5.png',
'static/guides/faq/insights/6.png',
'static/guides/faq/insights/7.png',
'static/guides/faq/insights/8.png',
'static/guides/faq/set-checks-to-default/1.png',
'static/guides/quick-guide/1.png',
'static/guides/quick-guide/2.png',
'static/guides/quick-guide/3.png',
'static/guides/quick-guide/4.png',
'static/guides/quick-guide/5.png',
'static/guides/quick-guide/6.png',
'static/guides/quick-guide/7.png',
'static/guides/quick-guide/8.png',
'static/guides/quick-guide/save-load-scenario.png',
'static/home/1.png',
'static/home/2.png',
'static/home/3.png',
'static/settings/equity-calculator-insights-not-visible.png',
'static/settings/equity-calculator-insights-visible.png',
'static/settings/outcome-analyzer-checkboxes-collapsed-1.png',
'static/settings/outcome-analyzer-checkboxes-collapsed-2.png',
'static/settings/outcome-analyzer-checkboxes-included-1.png',
'static/settings/outcome-analyzer-checkboxes-included-2.png',
'static/settings/outcome-analyzer-hands.png',
'static/settings/outcome-analyzer-insights-not-visible.png',
'static/settings/outcome-analyzer-insights-visible.png',
'static/settings/saved-ranges-1.png',
'static/settings/saved-ranges-2.png',
'static/settings/saved-ranges-3.png',
'static/settings/saved-ranges-4.png',
'static/settings/included-selectors/double-slider-selector.png',
'static/settings/included-selectors/log-double-slider-selector.png',
'static/settings/included-selectors/saved-ranges-selector.png',
'static/settings/included-selectors/single-slider-selector.png',
'static/settings/included-selectors/tier-and-category-selector.png',
'static/settings/tiers/tiers.png',
'static/settings/visual/dont-show-num-combos-in-range.png',
'static/settings/visual/green-grid-squares.png',
'static/settings/visual/multicolored-grid-squares.png',
'static/settings/visual/show-num-combos-in-range.png',
]).then(function () {
console.log('Successfully cached everything.')
}).catch(function (error) {
console.log('Problem caching: ', error);
});
return promise;
}).catch(function () {
console.error('Error with caches.open or cache.addAll');
})
);
});
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys()
.then(function getOldCachesThatBeginWithPremiumPokerToolsDash (cacheNames) {
return cacheNames.filter(function (cacheName) {
return cacheName.startsWith('premium-poker-tools-') && (cacheName !== currCacheName);
});
})
.then(function removeOldCachesThatBeginWithPremiumPokerToolsDash (oldCachesThatBeginWithPremiumPokerToolsDash) {
let removeCachePromises = [];
oldCachesThatBeginWithPremiumPokerToolsDash.forEach(function (oldCacheThatBeginsWithPremiumPokerToolsDash) {
removeCachePromises.push(caches.delete(oldCacheThatBeginsWithPremiumPokerToolsDash));
});
return Promise.all(removeCachePromises);
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function (response) {
if (response) {
return response;
}
return fetch(event.request);
}).catch(function () {
console.error('Error trying to match event request to cache.');
})
);
});
When I comment out 'c7d016677eb7e912bc40.worker.js' and 'f328c7e2b379df12fa4c.worker.js', it works fine. But when I uncomment them, here's what happens:
I start off going to the Dev Tools and unregistering any service workers and deleting anything in Cache Storage so that I start off with a clean slate.
I load localhost:8080.
The service worker stays in the installing state.
In Cache Storage, I see that c7d016677eb7e912bc40.worker.js and f328c7e2b379df12fa4c.worker.js have been successfully cached.
However, "Successfully cached everything." isn't being logged to the console. But "Problem caching: " isn't being logged either.
I understand that the promise you pass to event.waitUntil() lets the browser know when your install completes, and if it was successful. So clearly there is some issue going on with the promise. But I don't know what the issue is. It's not hitting the .then block or the .catch block, and when I look at Cache Storage in the Dev Tools, it appears that 'c7d016677eb7e912bc40.worker.js' and 'f328c7e2b379df12fa4c.worker.js' are being successfully cached.
Edit: I am using worker-loader. I get the feeling that the issue is related to how the worker files are loaded, but I don't see why there would be a problem, because when I go to localhost:8080/c7d016677eb7e912bc40.worker.js or localhost:8080/f328c7e2b379df12fa4c.worker.js, I get back the JS file.
Also, I have made sure that prefixes in c7d016677eb7e912bc40.worker.js and f328c7e2b379df12fa4c.worker.js are accurate. Eg. that c7d016677eb7e912bc40 and f328c7e2b379df12fa4c are accurate.
Update:
This doesn't work how you think it does:
caches.open(currCacheName).then(function(cache) {
let promise = cache.addAll([
You are defining a variable called promise here, but it doesn't replace cache, which is the promise which needs to be fulfilled.
You have a load of assets to cache and then:
'static/settings/visual/show-num-combos-in-range.png',
]).then(function () {
console.log('Successfully cached everything.')
}).catch(function (error) {
console.log('Problem caching: ', error);
});
You don't return anything here, so .then won't run, but it's not an error, so .catch won't either.
Your cache gets populated, but your promise for extendableEvent.waitUntil() is never settled, so installation never completes.
Either scrap your promise variable / console.logs and return the cache.addAll like this:
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(currCacheName).then(function(cache) {
return cache.addAll([
'/',
'app.js',
'static/settings/visual/show-num-combos-in-range.png',
])
})
);
});
Or make your function async, like this:
self.addEventListener('install', event => {
event.waitUntil(async function() {
const cache = await caches.open(currCacheName);
await cache.addAll([
'/',
'app.js',
'static/settings/visual/show-num-combos-in-range.png',
]);
}());
});
The question title says it all.
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/react-redux/sw.js').then(() => {
console.log('registered');
}, err => console.log(err));
}
EDIT
It looks like the root of the problem is the path
navigator.serviceWorker.register('/react-redux/sw.js')
if I move the sw code, so that I have
navigator.serviceWorker.register('swRoot.js').then(() => {
then everything works properly. I tried just about everything I can think of above, from dropping the leading slash in /react-redux, to adding a {scope: of './', '/', '/react-redux', and none worked (with some causing errors).
Does anyone know what config magic is needed to be able to load a service worker from somewhere other than the root of your domain?
and then my entire sw.js
self.addEventListener('install', function(event) {
console.log('hello');
try {
console.log('typeof System in install', typeof System);
} catch(e){}
console.log('caching');
event.waitUntil(
caches.open('v1').then(function(cache) {
console.log('caching - getting');
return cache.addAll([
'/react-redux/a.js'
]);
}).catch(function(error){ console.log('error', error) })
);
});
console.log('ADDING FETCH')
self.addEventListener('fetch', function(event) {
console.log('fetching ->', event.request);
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
return fetch(event.request);
})
);
});
I never ever get the console.log('fetching ->', event.request); message. I even added this silliness to try to force the issue.
setTimeout(() => fetch('/react-redux/foo.css').then(r => console.log(r)), 1000);
setInterval(() => fetch('/react-redux/foo.css').then(r => console.log(r)), 5000);
I see the fetch events running, but the service worker never says it's hitting those event handlers.
Moreover, I do get notified that the SW is registered, and when I update sw.js, close and re-open, I see all the logging statements indicating that things are installing correctly.
You're on the right track, it seems to be a scoping issue.
To change the scope of a service worker, it must be done when registering, like this:
navigator.serviceWorker.register('scripts/sw.js', { scope: '/' })
Then the server must return the following header in the response to acknowledge this scope change.
Service-Worker-Allowed: /
See this answer
Attach fetch event listener within install event handler
self.addEventListener('install', function(event) {
console.log("install");
try {
console.log('typeof System in install', typeof System);
} catch (e) {}
console.log('caching');
event.waitUntil(
caches.open('v1').then(function(cache) {
console.log('caching - getting');
return cache.addAll([
'a.js'
]);
}).catch(function(error) {
console.log('error', error)
})
);
self.addEventListener('fetch', function(event) {
console.log('fetching ->', event.request);
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
return fetch(event.request);
})
);
});
});
plnkr https://plnkr.co/edit/WuJCZSD0V4idG1Ra7VMb?p=preview
Getting fetch interception working seems to require that the registered
service worker be at or above the level of the tree with the HTML file
registering the worker. Below fails.
In Chrome, be sure to look at "Service Worker" under "Application".
Even registering below results in a Service Worker which is activated
and running, but when the location is at or above the HTML page, you
also get an entry listed under "Clients". The presence of this seems
to correspond exactly to when the fetch will be intercepted.