-
Notifications
You must be signed in to change notification settings - Fork 900
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Wait for service worker registration to become active before any operations #8661
Conversation
…e allowing any operations on it.
🦋 Changeset detectedLatest commit: 2a81f96 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Size Report 1Affected Products
Test Logs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm. thanks for the change!
return new Promise<void>((resolve, reject) => { | ||
if (registration.active) { | ||
resolve(); | ||
} | ||
const incomingSw = registration.installing || registration.waiting; | ||
if (incomingSw) { | ||
incomingSw.onstatechange = ev => { | ||
if ((ev.target as ServiceWorker)?.state === 'activated') { | ||
incomingSw.onstatechange = null; | ||
resolve(); | ||
} | ||
}; | ||
} else { | ||
reject(new Error('No incoming service worker found.')); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be extremely unlikely, but if the registration transitions to the active state after if(registration.active)
and before const incomingSw = registration.installing || registration.waiting
, I think we would reject and the registration would fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed it to an if/else but not sure if this affects the timing at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this is a tricky timing issue. I think now the SW could become active before we add the event listener, causing us to miss the activated
event. For example:
- registration is waiting
- registration is not yet active, so we don't resolve immediately on line 79.
- registration becomes active, and the
'activated'
event fires - attach registration state event listener
- event listener never fires because we missed the
'activated'
event and we reject the promise after 10s
This is just me speculating, I'm not sure if this order of events is possible.
Changeset File Check ✅
|
Size Analysis Report 1Affected Products
Test Logs |
Examples on MDN seem to claim that
serviceWorkerRegistration.pushManager.subscribe()
can be called right afterawait
ingnavigator.serviceWorker.register
, but that doesn't actually seem to be the case in practice. See issues #7784 and #7693.When the user calls
getToken()
(and is using the default service worker), we awaitregister()
inregisterDefaultSw()
and we go ahead and callpushManager.subscribe()
soon after. Unfortunately it's often not actually ready. Not sure if this is a bug - spec seems to indicate it should be ready?One option is to await
navigator.serviceWorker.ready
but this can be tricky depending on the scope of the service worker, where it may not consider your service worker the page's default service worker. It seems to hang in my test apps. It also may get mixed up with other service workers from other apps on the same page.Instead, we can listen for events on the specific service worker we just registered and check when the
state
becomes "active". Borrowed some working code for this from various contributors in #7693 (comment) (Thank you!)Fixes #7784
Fixes #7693