一个服务工作者中除事件处理程序外的代码何时运行?

41
(我在 "关于Service Worker我希望早些知道的事情" 摘要中转述了Rich Harris提出的问题。)
如果我的service worker中有在事件处理程序外运行的代码,它什么时候运行?
与此密切相关的是,在安装处理程序内放置和完全在事件处理程序之外放置之间有什么区别?
1个回答

74
通常情况下,位于服务工作者全局作用域的任何事件处理程序之外的代码将在每次启动服务工作者线程/进程时运行。服务工作者线程可能会在任意时间启动(和停止),它并不绑定到它所控制的Web页面的生命周期。
(频繁启动/停止服务工作者线程是一种性能/电池优化,可以确保,例如,仅因为您浏览到已注册服务工作者的页面,您不会得到额外的空闲线程在后台旋转。)
与此相关的反面是,每次停止服务工作者线程时,所有现有的全局状态都将被销毁。因此,虽然您可以进行某些优化,例如在全局状态中存储打开的IndexedDB连接,以期在多个事件之间共享它,但如果线程在事件处理程序调用之间被杀死,则需要准备重新初始化它们。
与这个问题密切相关的是我看到的一个误解,即安装事件处理程序。我见过一些开发人员使用安装处理程序来初始化全局状态,然后依赖于其他事件处理程序(如fetch)。这是危险的,并且很可能会导致生产中的错误。安装处理程序每个服务工作者版本只会触发一次,并且通常最适合用于与服务工作者版本控制相关的任务,例如缓存该版本需要的新资源或更新资源。成功完成安装处理程序后,给定服务工作者版本将被视为“已安装”,并且在服务工作者启动以处理fetch或message事件时不会再次触发安装处理程序。
因此,如果有全局状态需要在处理fetch事件之前初始化,可以在顶级服务工作者全局作用域中执行此操作(可选地等待promise解析到fetch事件处理程序内确保任何异步操作已完成)。不要依赖于安装处理程序来设置全局范围!
以下是说明其中一些要点的示例:
// 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`.
    });
  );
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接