Service worker only fetches url from cache when adding "index.html" - javascript

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);
})
);
}*/
});

Related

PWA TypeError: Failed to fetch

I am trying to install PWA on my website. Here is my Service worker:
const CACHE_NAME = 'cache_v1';
self.addEventListener('install', async event => {
self.skipWaiting();
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.open(CACHE_NAME).then(function(cache) {
if (navigator.onLine) {
return fetch(event.request).then(function(response) {
if (event.request.method === 'GET') {
cache.put(event.request, response.clone());
}
return response;
});
} else {
return cache.match(event.request).then(function (response) {
return response;
});
}
}),
);
});
This throws me an error:
TypeError: Failed to fetch
And warning:
Site cannot be installed: Page does not work offline. Starting in Chrome 93, the installability criteria is changing, and this site will not be installable. See https://goo.gle/improved-pwa-offline-detection for more information
How can I fix them?
Happy to help brother - you have not mentioned any files to be cached but you are fetching files that's why its giving errors try mine file:
service worker.js

How to make service worker network first then cache

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

Service Worker fetch event is not being triggered

self.addEventListener('install', function (event) {
console.log('SW Installed');
event.waitUntil(
caches.open('static')
.then(function (cache) {
cache.addAll([
'/',
'/static/js/renderjson.js',
"/static/css/app.css",
'/static/js/contribute.js',
"http://pending.biothings.io/denovodb/metadata",
"http://pending.biothings.io/ccle/metadata",
"http://pending.biothings.io/kaviar/metadata",
"http://pending.biothings.io/biomuta/metadata",
"http://pending.biothings.io/fire/metadata",
]);
})
);
});
self.addEventListener('activate', function () {
console.log('SW Activated');
});
self.addEventListener('fetch', function(event) {
console.log('FETCH',event.request);
event.respondWith(
caches.match(event.request)
.then(function(response) {
if (response) {
// retrieve from cache
console.log('Found ', event.request.url, ' in cache');
return response;
}
// if not found in cache, return default offline content (only if this is a navigation request)
if (event.request.mode === 'navigate') {
return caches.match('/');
}
// fetch as normal
console.log('Network request for ', event.request.url);
return fetch(event.request);
})
);
});
I'm new to PWA's so bear with me, I'm running a tornado server here is the registration:
if ('serviceWorker' in navigator) {
// Use the window load event to keep the page load performant
window.addEventListener('load', () => {
navigator.serviceWorker.register('/static/js/worker.js');
console.log('Service Worker registered');
});
}
I put a few console logs to see if the fetch event was being triggered but nothing seems to be happening, when I check the offline box on the Application tab and refresh the app does not work offline. All the assets are cached correctly and verified they are there. Looks like the last bit is just making the Worker server the right cached files.
For my case specifically, our site just needed to use https for the worker to activate properly. after doing that the listeners worked perfectly. I would recommend using Workbox, a lot easier!

Service worker won't install when I try to cache xxx.worker.js

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',
]);
}());
});

Service worker unregistered when offline

So I've created and successfully registered a Service Worker when the browser is online. I can see that the resources are properly cached using the DevTools. The issue is when I switch to offline mode, the service worker seems to unregister itself and, as such, nothing but the google chrome offline page is displayed.
The code.
'use strict';
var CACHE_NAME = 'v1';
var urlsToCache = [
'/'
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
// IMPORTANT: Clone the request. A request is a stream and
// can only be consumed once. Since we are consuming this
// once by cache and once by the browser for fetch, we need
// to clone the response.
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(
function(response) {
// Check if we received a valid response
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// 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;
}
);
})
);
});
And the test script, if that helps.
'use strict';
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
var serviceWorker;
if (registration.installing) {
serviceWorker = registration.installing;
} else if (registration.waiting) {
serviceWorker = registration.waiting;
} else if (registration.active) {
serviceWorker = registration.active;
}
if (serviceWorker) {
console.log('ServiceWorker phase:', serviceWorker.state);
serviceWorker.addEventListener('statechange', function (e) {
console.log('ServiceWorker phase:', e.target.state);
});
}
}).catch(function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
}
Edit: Checking the console I've found this error. sw.js:1 An unknown error occurred when fetching the script.
Aslo, as per a suggestion, I've added this code yet the problem persists.
this.addEventListener('activate', function(event) {
var cacheWhitelist = ['v2'];
event.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (cacheWhitelist.indexOf(key) === -1) {
return caches.delete(key);
}
}));
})
);
});
it seems you haven't added any activate event which meant to render cached elements when available. Hope the code help you.
self.addEventListener('activate', function(e) {
/*service worker activated */
e.waitUntil(
caches.key().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if(key){
//remove old cache stuffs
return caches.delete(key);
}
}));
})
);
});
The problem appears to have fixed itself. To anyone here from google, try restarting the browser.

Categories