Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

187
Views
Prompting only once when there are multiple workbox-broadcast-update messages

I'm using Workbox and the BroadcastUpdatePlugin() in my serviceWorker to prompt the user to refresh the page when a cached file is updated. It works great when there's only one file updated, but when I publish multiple updates at once (HTML, CSS and JS files), the user is prompted to refresh the page for each file.

How can I update all files in the cache, then prompt the user to refresh the page only once, when the event listener has stopped receiving update messages?

ServiceWorker code

const {BroadcastUpdatePlugin} = workbox.broadcastUpdate;

registerRoute(
  ({request}) => request.destination === 'document',
  new StaleWhileRevalidate({
  //new NetworkOnly({
    cacheName: 'pages',
    plugins: [
      new BroadcastUpdatePlugin(),
    ],
  })
)

registerRoute(
  ({request}) => request.destination === 'script' || request.destination === 'style',
  new StaleWhileRevalidate({
  //new NetworkOnly({
    cacheName: 'assets',
    plugins: [
      new BroadcastUpdatePlugin(),
    ],
  })
)

JavaScript code

if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('sw.js', { scope: '/' }).then(swReg => {
            console.log('Service Worker Registered', swReg);
        }).catch(error => {
            console.log('There was an error!', error);
        })
    })
        
    // Listen for cache updates and prompt a page reload
    navigator.serviceWorker.addEventListener('message', async (event) => {
        if (event.data.meta === 'workbox-broadcast-update') {
            const {cacheName, updatedURL} = event.data.payload;
            const cache = await caches.open(cacheName);
            const updatedResponse = await cache.match(updatedURL);
            const updatedText = await updatedResponse.text();
            console.log('Updated: '+cacheName+', '+updatedURL);

            // prompts for every update
            if(confirm('Content Updated. Please refresh the page.')){
                window.location.reload;
            }
        }
    })
}
about 4 years ago · Juan Pablo Isaza
1 answers
Answer question

0

I could imagine there potentially being logic in your service worker that could help with this, by, for instance, looking at the cliendId in the FetchEvent that triggered an update and only conditionally sending the message if it hasn't seen that clientId before.

But that would require a bunch of custom logic beyond what BroadcastUpdatePlugin provides already, and it might be error prone—if your service worker broadcasts an update, then the user acts on the update, and then there's another update that could be broadcast, you probably want to give the user another chance to act on it, but it would be difficult for the service worker to know whether to broadcast a message to the same clientId in that scenario.

A cleaner approach would be to move the logic that prevented multiple prompt to the window client code.

There are a couple of ways to go there, but what seems the cleanest to me would rely on the resolution of a promise to trigger the prompt. You can call the resolve function of a promise multiple times, but the then() in the promise chain will only be invoked once, giving you the behavior you're looking for.

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js');
  });

  const waitForUpdate = new Promise((resolve) => {
    navigator.serviceWorker.addEventListener('message', async (event) => {
      if (event.data.meta === 'workbox-broadcast-update') {
        const {cacheName, updatedURL} = event.data.payload;
        const cache = await caches.open(cacheName);
        const updatedResponse = await cache.match(updatedURL);
        const updatedText = await updatedResponse.text();
        console.log(`Updated ${updatedURL} in ${cacheName}: ${updatedText}`);

        // Calling resolve() will trigger the promise's then() once.
        resolve();
      }
    });
  });

  waitForUpdate.then(() => {
    if (confirm('Content updated. Please refresh the page.')) {
      window.location.reload();
    }
  });
}

(You could also use an approach that relied on a global variable or something similar that gets flipped the first time you show the prompt, and short-circuits the prompt display every subsequent time, but I like using promises for this sort of thing.)

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!