w3c / ServiceWorker

Service Workers
https://w3c.github.io/ServiceWorker/
Other
3.63k stars 313 forks source link

Provide an easier way to listen for waiting/activated/redundant Service Workers #1247

Open dfabulich opened 6 years ago

dfabulich commented 6 years ago

Breaking off from #1222.

ServiceWorker.state has five states: installing, installed (waiting), activating, activated, and redundant.

Today, client-side code can listen for installing SWs with ServiceWorkerRegistration.onupdatefound; activating can be tracked with navigator.serviceWorker.oncontrollerchange.

The other three states are annoying to listen for.

function listenForStateChanges(reg, callback) {
  if (reg.installing) reg.installing.addEventListener('statechange', callback);
  if (reg.waiting) reg.waiting.addEventListener('statechange', callback);
  reg.addEventListener('updatefound', function() {
    reg.installing.addEventListener('statechange', callback);
  });
}

That's too bad, because lots of users want to track the installed state so they can show a refresh banner. Furthermore, some users apparently want to track redundant SWs for error handling. And it turns out that refreshing the page as the page is activating is slightly too soon; you should wait for the activated event, to avoid a minor performance bug.

In #1222 we observed that awaiting these states would be way easier if statechange bubbled up to the registration, so users who wanted to track the redundant and activated states would just do this:

reg.addEventListener('statechange', callback);
dfabulich commented 6 years ago

Alerting users of a waiting service worker might look like this:

Combined with #1016, that means that showing a "please refresh" banner could work like this:

function alertUser(reg) {
  // don't use confirm in production; this is just an example
  if (confirm('Refresh now?')) reg.waiting.skipWaiting();
}

if (reg.waiting) alertUser(reg);
reg.addEventListener('statechange', function(e) {
  if (e.target.state === 'installed') {
    alertUser(reg);
  } else if (e.target.state === 'activated') {
    window.location.reload();
  }
});
ouksal commented 5 years ago

@dfabulich I like your suggestion. I can't believe nobody has been assigned to fix this!

jakearchibald commented 5 years ago

A promise doesn't really work in this case, as many service workers can pass through the waiting phase during the life of a page.

dfabulich commented 5 years ago

To be clear, this issue #1247 doesn't involve Promises; it calls for the statechange event to bubble up to the registration. Issue #1222 recommends a Promise; I'll copy and paste your comment there.

dfabulich commented 5 years ago

In fact, this issue was originally @jakearchibald's idea, in a comment on #1222.

As far as I can tell, nobody has had anything bad to say about it. I might have preferred #1222, but since that approach seems to be dead in the water, this is still a big improvement over the status quo.

daffinm commented 4 years ago

+1 for @dfabulich prolyfil idea. 3 years old and still needed IMO.