3

I have a simple service worker with button update when new service worker is in queue. After update I want to make page refresh on controllerchange but nothing happens. All in all service worker is updated live but following that course (https://www.udacity.com/course/offline-web-applications--ud899) they try to add that refresh for some reason.

In js:

const updateButton = document.querySelector('.js-update-btn')

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('sw.js')
  .then(function (reg) {
    if (!navigator.serviceWorker.controller) {
      return
    }

    if (reg.waiting) {
      updateReady(reg.waiting)
      return
    }

    if (reg.installing) {
      trackInstalling(reg.installing)
      return
    }

    reg.addEventListener('updatefound', function () {
      trackInstalling(reg.installing)
    })
  })

  var refreshing
  navigator.serviceWorker.addEventListener('controllerchange', function () {
    if (refreshing) return
    window.location.reload()
    refreshing = true
  })
}

function updateReady (worker) {
  updateButton.disabled = false

  updateButton.addEventListener('click', function () {
    updateButton.disabled = true
    worker.postMessage({action: 'skipWaiting'})
  })
}

function trackInstalling (worker) {
  worker.addEventListener('statechange', function () {
    if (worker.state == 'installed') {
      updateReady(worker)
    }
  })
}

In sw.js:

self.addEventListener('message', function (event) {
  if (event.data.action == 'skipWaiting') {
    self.skipWaiting()
  }
})
Jeff Posnick
  • 53,580
  • 14
  • 141
  • 167
Krystus
  • 285
  • 3
  • 4
  • 14

2 Answers2

10

I would recommend following the example from the sw-precache demo code.

Here's a very concise, modified version of that example that will force a page reload when there's an update to an already installed service worker. (I'd argue that it might be more user-friendly to display a message on the page asking the user to reload manually, though, so that you don't end up interrupting whatever they were doing on the page.)

navigator.serviceWorker.register('/service-worker.js').then(reg => {
  reg.onupdatefound = () => {
    const installingWorker = reg.installing;
    installingWorker.onstatechange = () => {
      if (installingWorker.state === 'installed' &&
          navigator.serviceWorker.controller) {
        // Preferably, display a message asking the user to reload...
        location.reload();
      }
    };
  };
});
Jeff Posnick
  • 53,580
  • 14
  • 141
  • 167
  • 1
    This doesn't work when page is refreshed via Ctrl+F5 as navigator.serviceWorker.controller is null in the `installed` phase (but has proper value in 'activated' phase) – piotr_cz Jun 19 '20 at 13:42
3

After many attempts I come with something like that.

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('sw.js')
  .then(function (reg) {
    if (!navigator.serviceWorker.controller) {
      return
    }

    reg.addEventListener('updatefound', function () {
      const newWorker = reg.installing
      newWorker.state

      var refreshing

      newWorker.addEventListener('statechange', () => {
        if (newWorker.state == 'activated') {
          if (refreshing) return
          window.location.reload()
          refreshing = true
        }
      })

      trackInstalling(reg.installing)
    })
  })
}

It's working. Is there any flaws of that code?

Krystus
  • 285
  • 3
  • 4
  • 14