Sometimes registration of the service worker (SW) hangs. Since our PWA relies on the SW, and won't start without it, it looks like our App hung on startup. It seems I need to code an escape hatch. I seek suggestions.
The fact that the registration hangs was established by inserting 90 second timeout, as you see below.
const serviceWorker = navigator.serviceWorker;
if( serviceWorker && typeof serviceWorker.register === 'function' ) {
swRegisterTimeout = setTimeout(swRegTimedOut,90000);
serviceWorker.register('serviceworker.js')
.then(onServiceworkerRegistered)
.catch(function(err){
clearTimeout(swRegisterTimeout);
message("Service Worker Register Failure");
})
} else {
if ( serviceWorker ) {
message("'serviceWorker.register' is not a function")
}else{
message("'navigator.serviceWorker' does not exist")
}
}
});
function swRegTimedOut(){
message("ServiceWorker unable to register in 90 seconds")
}
function onServiceworkerRegistered(){
var serviceWorker = navigator.serviceWorker,
postBox = serviceWorker.controller;
clearTimeout(swRegisterTimeout);
message("Service Worker Registered");
serviceWorker.ready.then(function(reg){
if(reg.active){
// More stuff
}
})
}
This happens only when our internet is operable, but barely. Then I get the hang - with the timeout message ninety seconds later - every time, and the App does not launch.
If there is no internet, no hang; everything starts instantly. In fact, I can break the hang by turning off Wifi on the R&D machine. Boom! Everything loads immediately.
I speculate that somewhere in the registration code, I/O is being attempted. If said I/O does not fail or succeed, the registration does not come back.
In order to selectively bypass the registration step, our code would need to be able to recognize the difference between a hang and a valid registration. We gotta be careful here; I don't wanna write something that will break support for other browsers.
EDIT: This is only a concern if a serviceworker is in place. It goes without saying that I do not expect to load a fresh service worker and the initial files on a near-dead network.
CodePudding user response:
Background
Let's break down what navigator.serviceWorker.register()
does, and what the promise that it returns represents.
The definitive set of steps is documented in the service worker specification, beginning with the "Start Register" job. There is slightly different behavior depending on whether the service worker scriptURL
and scope
have been previously registered or not, so the first thing to ask yourself is whether you're seeing this only when you're registering for the first time, in a "clean" browser, or whether you also see it when there's an identical, pre-existing service worker registration.
Either way, the promise that navigator.serviceWorker.register()
returns fulfills once it's clear that the registration is for a script resource that's valid JavaScript with a scope
that follows the security restrictions.
The promise will reject if you attempt to register a JavaScript file with invalid syntax, or if you use a scope
that's not compatible with the service worker restrictions, or if there is a network error that prevents the JavaScript from being requested.
It's important to note that the way the navigator.serviceWorker.register()
resolves does not reflect whether the service worker being registered actually installed correctly. It's possible to register a service worker script that does not contain any syntax errors and has a valid scope (so the navigator.serviceWorker.register()
will fulfill), but which becomes redundant
during the install
phase, perhaps because the promise it passes to installEvent.waitUntil()
ends up rejecting. The fact that navigator.serviceWorker.register()
fulfills does imply that you'll always have an installed
service worker!
Using navigator.serviceWorker.ready independently of registration
So, with that out of the way, let's get to your specific code snippet. If you're seeing the promise returned by navigator.serviceWorker.register()
rejected after a long delay when you have a flaky network connection, it's almost certainly being rejected because the network request for the service worker script ends up timing out. There's really not much you could do about this scenario—that's the definition of what happens when you have a flaky network, and to ensure that your service worker is always "fresh", the request for the service worker script will bypass any caches by default.
I think the problem you're running into, though, is that you're using navigator.serviceWorker.ready
execute some code once there's an active
service worker, but you're only calling it if the navigator.serviceWorker.register()
promise fulfills. For the reasons explained above, I don't think you should structure your code that way.
Instead, I would just break up your code into two steps—one that calls navigator.serviceWorker.register()
, and one that waits for navigator.serviceWorker.ready
to fulfill, independent of each other.
navigator.serviceWorker.register()
.then(() => console.log('Registration succeeded.')
.catch((error) => console.log('Registration failed: ', error));
navigator.serviceWorker.ready.then(() => {
// Put whatever code requires an active service worker here.
});