akio-t
akio-t

Reputation: 11

workbox Cache processing does not work with PrecacheController in v6

I was using workbox5.14 (@nuxtjs/pwa) to precache the 'message' event by passing the URL to PrecacheController when received. (from workbox-window) At this time, PrecacheController.addToCacheList() would not execute the cache, and for some reason PrecacheController.install() would do so.

However, with v6, PrecacheController.install() now requires an 'install' event or an 'activate' event, and I cannot execute PrecacheController.install(event) with a 'message' event. This results in an error. "sw.js:238 Uncaught (in promise) DOMException: Failed to execute 'waitUntil' on 'ExtendableEvent': The event handler is already finished and no extend lifetime promises are outstanding."

How can we execute PrecacheController's caching process on the 'message' event?

Library Affected: workbox-precaching

Browser & Platform: Google Chrome 106.0.5249.103(Official Build)

Issue or Feature Request Description: v5 ok

/* global importScripts, workbox, consola, processFuncPromise */

const cacheName = workbox.core.cacheNames.precache
const precacheController = new workbox.precaching.PrecacheController(cacheName)

addEventListener('message', async (event) => {
  if (event.data.type === 'ADD_PRECACHE') {
    const cacheTargetFiles = event.data.payload

    const addCaches = async (cacheTargetFiles) => {
      for (const file of cacheTargetFiles) {
        const cacheKey = precacheController.getCacheKeyForURL(file.filePath)
        const cached = await caches
          .match(cacheKey)
          .then((response) => response !== undefined)
        if (!cached) {
          precacheController.addToCacheList([{ url: file.filePath, revision: file.revision }])
        }
      }
    }

    const checkCaches = async () => {
      const cachedList = cacheTargetFiles.map(async (file) => {
        const cacheKey = precacheController.getCacheKeyForURL(file.filePath)
        const cached = await caches
          .match(cacheKey)
          .then((response) => response !== undefined)
        return cached
      })
      const cachedListResult = await Promise.all(cachedList)

      const cachedListResultFilterd = cachedListResult.filter((response) => {
        return response
      })

      if (cachedListResultFilterd.length === cacheTargetFiles.length) {
        return Promise.resolve({ isCompleted: true })
      } else {
        return Promise.resolve(null)
      }
    }

    await addCaches(cacheTargetFiles)

    // ★Caching is started with this INSTALL
    precacheController.install()

    await processFuncPromise(checkCaches)

    self.clients.matchAll().then((clients) =>
      clients.forEach((client) => {
        client.postMessage({ type: 'FINISHED_ADD_PRECACHE' })
      })
    )
  }
})

addEventListener('install', (event) => {
  event.waitUntil(precacheController.install())
  event.waitUntil(self.skipWaiting())
})

addEventListener('activate', (event) => {
  workbox.precaching.cleanupOutdatedCaches()
  event.waitUntil(precacheController.activate())
  event.waitUntil(self.clients.claim())
})

addEventListener('fetch', (event) => {
  const cacheKey = precacheController.getCacheKeyForURL(event.request.url)
  event.respondWith(
    caches.match(cacheKey).then(function (response) {
      // Cache hit - return the response from the cached version
      if (response) {
        return response
      }
      // Not in cache - return the result from the live server
      // `fetch` is essentially a "fallback"
      return fetch(event.request)
    })
  )
})


// ↓processFuncPromise()
// export const processFuncPromise = (func, interval = 500) => {
//   const retryFunc = (resolve, reject) =>
//     func()
//       .then((result) => ({ result, isCompleted: result !== null }))
//       .then(({ result, isCompleted }) => {
//         if (isCompleted) {
//           return resolve(result)
//         } else {
//           return setTimeout(() => retryFunc(resolve, reject), interval)
//         }
//       })
//       .catch(reject)

//   return new Promise(retryFunc)
// }

v6 ng

/* global importScripts, workbox, consola, processFuncPromise */

const cacheName = workbox.core.cacheNames.precache
const precacheController = new workbox.precaching.PrecacheController(cacheName)

addEventListener('message', async (event) => {
  if (event.data.type === 'ADD_PRECACHE') {
    const cacheTargetFiles = event.data.payload

    const addCaches = async (cacheTargetFiles) => {
      for (const file of cacheTargetFiles) {
        const cacheKey = precacheController.getCacheKeyForURL(file.filePath)
        const cached = await caches
          .match(cacheKey)
          .then((response) => response !== undefined)
        if (!cached) {
          precacheController.addToCacheList([{ url: file.filePath, revision: file.revision }])
        }
      }
    }

    const checkCaches = async () => {
      const cachedList = cacheTargetFiles.map(async (file) => {
        const cacheKey = precacheController.getCacheKeyForURL(file.filePath)
        const cached = await caches
          .match(cacheKey)
          .then((response) => response !== undefined)
        return cached
      })
      const cachedListResult = await Promise.all(cachedList)

      const cachedListResultFilterd = cachedListResult.filter((response) => {
        return response
      })

      if (cachedListResultFilterd.length === cacheTargetFiles.length) {
        return Promise.resolve({ isCompleted: true })
      } else {
        return Promise.resolve(null)
      }
    }

    await addCaches(cacheTargetFiles)

    // ★ERROR
    precacheController.install(event)

    await processFuncPromise(checkCaches)

    self.clients.matchAll().then((clients) =>
      clients.forEach((client) => {
        client.postMessage({ type: 'FINISHED_ADD_PRECACHE' })
      })
    )
  }
})

addEventListener('install', (event) => {
  precacheController.install(event)
  event.waitUntil(self.skipWaiting())
})

addEventListener('activate', (event) => {
  workbox.precaching.cleanupOutdatedCaches()
  precacheController.activate(event)
  event.waitUntil(self.clients.claim())
})

addEventListener('fetch', (event) => {
  const cacheKey = precacheController.getCacheKeyForURL(event.request.url)
  event.respondWith(
    caches.match(cacheKey).then(function (response) {
      // Cache hit - return the response from the cached version
      if (response) {
        return response
      }
      // Not in cache - return the result from the live server
      // `fetch` is essentially a "fallback"
      return fetch(event.request)
    })
  )
})


// ↓processFuncPromise()
// export const processFuncPromise = (func, interval = 500) => {
//   const retryFunc = (resolve, reject) =>
//     func()
//       .then((result) => ({ result, isCompleted: result !== null }))
//       .then(({ result, isCompleted }) => {
//         if (isCompleted) {
//           return resolve(result)
//         } else {
//           return setTimeout(() => retryFunc(resolve, reject), interval)
//         }
//       })
//       .catch(reject)

//   return new Promise(retryFunc)
// }

I asked the question on githubbut could not get an answer, so I came here.

Upvotes: 1

Views: 326

Answers (0)

Related Questions