Создает ли использование postMessage и выборки со страницы условие гонки с ServiceWorkers? - PullRequest
1 голос
/ 06 февраля 2020

Я новичок в ServiceWorkers, но я хотел сохранить мою домашнюю страницу как можно более свободной sh, используя кэш, а затем сетевой шаблон, как показано в автономной кулинарной книге Google .

Однако вместо потоков данных я использую stati c html, а исходный код, связанный с документацией Google, не использует этот пример. Итак, я разработал шаблон, который работает в тестировании, но я обеспокоен тем, что он вводит условие гонки.

Шаблон, который я пытаюсь достичь:

  1. Запрос
  2. ServiceWorker возвращает кэшированный ответ
  3. Страница отправляет сообщение в ServiceWorker с указанием использовать сеть при следующей загрузке
  4. Страница делает fetch запрос
  5. Обновления ServiceWorker кеширует и возвращает сетевой ответ
  6. Обновления страницы с сетевым ответом

Однако может быть условие гонки между 3 и 4 .

Проблема в том, что 3 и 4 происходят в клиенте, в то время как 5 происходит в ServiceWorker.

Я не уверен в потенциальной задержке, вызванной postMessage(). Если это существенно, 4 будет извлекать страницу до того, как ServiceWorker узнает, что она должна извлекаться из сети, и будет возвращен повторный кэшированный ответ.

Это так? Или у ServiceWorker всегда будет время обновить флаг с postMessage() до следующего запроса на выборку?

Source

Page

let isControlled = navigator.serviceWorker.controller;
let isHomepage = location.href === location.origin + '/';
let homepageCached = getCookie('homecached') === 'true';

if (isControlled && !homepageCached) {

  // If the cookie is expired or unset, push an update to the service worker and update cookie
  navigator.serviceWorker.controller.postMessage('updateHomepageCache');
  // `1` means store the cookie for 1 day
  setCookie('homecached', 'true', 1);

  if (isHomepage) {

    // This works easily with location.reload(), but it's sloppy and causes a flicker
    let existingMain = document.querySelector('.main');
    fetch(location.href)
    .then(function(response) {
      return response.text();
    }).then(function(text) {
      try {

        // Convert response text to a new document
        let parser = new DOMParser();
        let doc = parser.parseFromString(text, 'text/html');

        // Get `main` element of response and update the current document with it
        let fetchedMain = doc.querySelector('.main');
        let parent = existingMain.parentNode;
        parent.replaceChild(fetchedMain, existingMain);

      } catch (err) {
        console.error(err);
      }
    });
  }
}

Сервисный работник


// Set flag to false so update only happens when cache is invalid
let updateHomepageCache = false;

self.addEventListener('message', (event) => {

  // Update flag if cache is invalid
  // Note, this comes after fetch in the ServiceWorker's lifecycle
  if (event.data === 'updateHomepageCache') {
    updateHomepageCache = true;
  }
});

self.addEventListener('fetch', (event) => {

  const normalizedUrl = new URL(event.request.url);
  normalizedUrl.search = '';

  const isNavgation = event.request.mode === 'navigate';
  const isFromOrigin = normalizedUrl.origin === location.origin;
  const isHomepage = normalizedUrl.href === location.origin + '/';

  // Respond with network and update cache for homepage (if it needs to be updated)
  if (isHomepage && isNavgation && updateHomepageCache) {
    event.respondWith(
      caches.open(cacheName).then(function(cache) {
        return fetch(normalizedUrl).then(function(networkResponse) {
          cache.put(normalizedUrl, networkResponse.clone());
          updateHomepageCache = false;
          return networkResponse;
        }).catch(function() {
          return cache.match(normalizedUrl);
        });
      })
    );

  // Cache then update cache with network response for pages "stale-while-revalidate"
  } else if (isFromOrigin && isNavgation) {
    event.respondWith(
      caches.open(cacheName).then(function(cache) {
        return cache.match(normalizedUrl).then(function(response) {
          let fetchPromise = fetch(normalizedUrl).then(function(networkResponse) {
            cache.put(normalizedUrl, networkResponse.clone());
            return networkResponse;
          });
          return response || fetchPromise;
        });
      })
    );
});
...