当前位置: 首页 > 知识库问答 >
问题:

前端 - service worker 中 event.waitUntil() 方法的一些问题?

颛孙飞鸾
2024-08-19

在 service worker 中的 install 和 active 事件中事件对象会有 event.waitUntil() 方法,该对象方法可以存入一个 promise 对象,在这个 promise 没有返回期间,promise 内执行的操作不会触发 fetch 事件。我发现如果在 event.waitUntil()方法执行之前使用了 await 就会触发下列的错误

DOMException: Failed to execute 'waitUntil' on 'ExtendableEvent': The event handler is already finished and no extend lifetime promises are outstanding.

但是把 await 删除就不会触发这样的错误。为什么会这样设计?

self.addEventListener('activate', async (event) => {
  console.log('service worker 被 激活')
  const windowsClients = await self.clients.matchAll({
    includeUncontrolled: true
  })
  event.waitUntil(Promise.all([enableNavigationPreload(), clients.claim()]))
})

共有1个答案

养俊驰
2024-08-19

回答问题

在 Service Worker 中,event.waitUntil() 方法的设计是为了允许开发者延长事件的生命周期,直到传递给它的 Promise 被解决(resolved)。这主要用于 installactivate 事件,确保这些事件中的关键操作(如缓存文件、清理旧缓存等)完成后再让 Service Worker 进入下一个状态。

为什么会出现错误?

错误 DOMException: Failed to execute 'waitUntil' on 'ExtendableEvent': The event handler is already finished and no extend lifetime promises are outstanding. 发生的原因是,当你在事件处理函数中使用了 await 关键字时,它会暂停当前函数的执行,并等待 Promise 解决。然而,如果在 await 调用之后(即 Promise 解决之前)事件处理函数已经完成了执行(即函数体内的代码执行完毕,包括所有同步和已解决的异步代码),那么 ExtendableEventinstallactivate 事件的对象类型)的生命周期就会被认为已经结束。

在你的示例中:

self.addEventListener('activate', async (event) => {
  console.log('service worker 被 激活')
  const windowsClients = await self.clients.matchAll({
    includeUncontrolled: true
  })
  // 当到达这里时,如果 windowsClients 的 Promise 已经解决,但事件处理函数还未调用 event.waitUntil(),
  // 则事件的生命周期可能已经结束,导致调用 event.waitUntil() 时抛出错误。
  event.waitUntil(Promise.all([enableNavigationPreload(), clients.claim()]))
})

解决方案

确保在事件处理函数结束之前调用 event.waitUntil(),并且所有可能影响其生命周期的异步操作都应该在调用 event.waitUntil() 之前完成或包含在其 Promise 数组中。你可以通过调整代码顺序来实现这一点:

self.addEventListener('activate', async (event) => {
  console.log('service worker 被 激活');
  // 将所有需要等待的操作放入同一个 Promise.all 中
  event.waitUntil(Promise.all([
    self.clients.matchAll({ includeUncontrolled: true }), // 虽然这里的结果可能未直接使用,但可以避免错误
    enableNavigationPreload(),
    clients.claim()
  ]).then(() => {
    // 这里可以处理匹配到的 windowsClients,但不影响 event.waitUntil 的行为
  }));
});

注意,虽然在这个特定的例子中,self.clients.matchAll() 的结果可能并未在后续代码中被直接使用,但将其包含在 Promise.all 中可以避免因为等待其完成而导致的事件生命周期提前结束的问题。同时,这也确保了所有必要的异步操作都在 event.waitUntil() 的控制之下。

 类似资料:
  • 当页面中存在多个css动画,css动画过于复杂,或者电脑性能不足时,都有可能导致动画卡顿。对css动画的优化方法都有哪些?

  • 本文向大家介绍python中类的一些方法分析,包括了python中类的一些方法分析的使用技巧和注意事项,需要的朋友参考一下 本文实例分析了python中类的一些方法,分享给大家供大家参考。具体分析如下: 先来看看下面这段代码: 本文实例运行环境为Python2.7.6 运行结果如下: in Provider.action  在Super类中定义delegate()方法,delegate中调用sel

  • TS中在抽象类中是否可以写一些已经实现的方法? 这个代码是一个抽象类BaseEdge,在里面有方法updateCache,这个是已经实现了的。也就是说抽象类中可以有已经实现好的方法是吗?

  • 本文向大家介绍一些基础的js 方法?相关面试题,主要包含被问及一些基础的js 方法?时的应答技巧和注意事项,需要的朋友参考一下 参考回答: Function.prototype.a = 1; Object.prototype.b = 2; function A() {} var a = new A(); console.log(a.a, a.b); // undefined, 2 console.

  • https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_flow_lay... 上面的文章写到: 1.常规流中:块级盒子是在块格式上下文(BFC)中的,而内联盒子是在内联格式上下文中的(IFC); 2.块格式上下文(BFC)中,盒子是垂直排列的; 3.内联格式上下文(IFC)中,盒子水平排列; 那么问题来了: 例子:通过display:flow-r

  • 我有一个关于JMS和Spring集成的问题。 我有3个队列,让我们称它们为QUEUE_SOURCE、QUEUE_TARGET和QUEUE_ERROR。DefaultMessageListenerContainer用于从队列源读取消息。 我已经为这些队列配置了JMS事务管理器。 当我从QUEUE_源读取消息,但将消息发布到QUEUE_目标时出错时,我可以看到在抛出异常之前,消息会重试几次,从而触发回