Когда выполняется код в рабочем рабочем столе вне обработчика события?

(Я перефразирую вопрос, заданный Ричем Харрисом в разделе " "Мне жаль, что я раньше не знал об обслуживающих рабочих" ).

Если у меня есть код в моем сервисном работнике, который работает за пределами обработчика событий, когда он запускается?

И, тесно связанный с этим, в чем разница между помещением внутри обработчика install и полностью помещением его вне обработчика события?

Ответы

Ответ 1

В общем случае код, который за пределами любого обработчика событий, на "верхнем уровне" глобальной области рабочего стола службы, запускается каждый раз, когда запускается поток (/процесс) рабочего рабочего. Поток обслуживающего персонала может запускаться (и останавливаться) в произвольные моменты времени и не привязываться к времени жизни контролируемых им веб-страниц.

(Запуск/остановка рабочего потока службы часто представляет собой оптимизацию производительности/аккумулятора и гарантирует, что, например, только потому, что вы просматриваете страницу, на которой зарегистрирован рабочий сервис, вы не получите лишнюю нисходящую нить, вращающуюся в фон.)

Отражающая сторона этого заключается в том, что каждый раз, когда поток рабочего пользователя останавливается, любое существующее глобальное состояние уничтожается. Поэтому, хотя вы можете сделать определенные оптимизации, например, хранить открытое соединение IndexedDB в глобальном состоянии, надеясь разделить его на несколько событий, вам нужно быть готовым повторно инициализировать их, если поток был убит между вызовами обработчика событий.

Близко связанный с этим вопрос - это неправильное представление, которое я видел о обработчике событий install. Я видел, как некоторые разработчики использовали обработчик install для инициализации глобального состояния, которое затем полагается на другие обработчики событий, например fetch. Это опасно и, вероятно, приведет к ошибкам в производстве. Обработчик install запускается один раз для версии рабочего-службы и обычно лучше всего используется для задач, привязанных к версиям, поддерживающим работоспособность, например, кэширование новых или обновленных ресурсов, необходимых для этой версии. После успешного завершения обработчика install указанная версия рабочего сотрудника будет считаться "установленной", а обработчик install не будет запущен снова, когда рабочий сервис начнет обрабатывать, например, fetch или message.

Итак, если есть глобальное состояние, которое должно быть инициализировано перед обработкой, например, событие fetch, вы можете сделать это в глобальной области рабочего стола службы верхнего уровня (необязательно ожидая обещания разрешить внутри fetch, чтобы гарантировать, что все асинхронные операции завершены). Do не полагаться на обработчик install для настройки глобальной области действия!

Вот пример, иллюстрирующий некоторые из этих пунктов:

// Assume this code lives in service-worker.js

// This is top-level code, outside of an event handler.
// You can use it to manage global state.

// _db will cache an open IndexedDB connection.
let _db;
const dbPromise = () => {
  if (_db) {
    return Promise.resolve(_db);
  }

  // Assume we're using some Promise-friendly IndexedDB wrapper.
  // E.g., https://www.npmjs.com/package/idb
  return idb.open('my-db', 1, upgradeDB => {
    return upgradeDB.createObjectStore('key-val');
  }).then(db => {
    _db = db;
    return db;
  });
};

self.addEventListener('install', event => {
  // `install` is fired once per version of service-worker.js.
  // Do **not** use it to manage global state!
  // You can use it to, e.g., cache resources using the Cache Storage API.
});

self.addEventListener('fetch', event => {
  event.respondWith(
    // Wait on dbPromise to resolve. If _db is already set, because the
    // service worker hasn't been killed in between event handlers, the promise
    // will resolve right away and the open connection will be reused.
    // Otherwise, if the global state was reset, then a new IndexedDB
    // connection will be opened.
    dbPromise().then(db => {
      // Do something with IndexedDB, and eventually return a `Response`.
    });
  );
});