Chrome 中的 Serviceworker 注册有时会挂起

Serviceworker registration in Chrome sometimes hangs

有时 service worker (SW) 的注册会挂起。 由于我们的 PWA 依赖于软件,没有它就无法启动,看起来我们的应用程序在启动时挂起了。 看来我需要编写一个逃生舱口。 我寻求建议。

注册挂起的事实是通过插入 90 秒超时确定的,如下所示。

  const serviceWorker = navigator.serviceWorker;
  if( serviceWorker && typeof serviceWorker.register === 'function' ) {
    swRegisterTimeout = setTimeout(swRegTimedOut,90000);
    serviceWorker.register('serviceworker.js')
                 .then(onServiceworkerRegistered)
                 .catch(function(err){
                        clearTimeout(swRegisterTimeout);
                        message("Service Worker Register Failure");
                    })
  } else {
    if ( serviceWorker ) {
      message("'serviceWorker.register' is not a function")
    }else{
      message("'navigator.serviceWorker' does not exist")
    }
  }
});
function swRegTimedOut(){
  message("ServiceWorker unable to register in 90 seconds")
}
function onServiceworkerRegistered(){
  var   serviceWorker = navigator.serviceWorker,
        postBox       = serviceWorker.controller;

  clearTimeout(swRegisterTimeout);
  message("Service Worker Registered");
  serviceWorker.ready.then(function(reg){
    if(reg.active){
      // More stuff
    }
  })
}

只有当我们的互联网可以运行时才会发生这种情况,但几乎没有。 然后我每次都挂起 - 九十秒后出现超时消息 - 并且应用程序无法启动。

如果没有网络,不挂;一切立即开始。 事实上,我可以通过关闭研发机器上的 Wifi 来打破挂起。 繁荣! 一切立即加载。

我推测在注册码的某处,I/O 正在被尝试。 如果说I/O不失败也不成功,注册不回来

为了有选择地绕过注册步骤,我们的代码需要能够识别挂起和有效注册之间的区别。 我们在这里要小心;我不想写一些会破坏对其他浏览器的支持的东西。

编辑:只有在服务人员到位的情况下,这才是一个问题。 不用说,我不希望在几乎死机的网络上加载新的 service worker 和初始文件。

背景

让我们分解一下 navigator.serviceWorker.register() 的作用,以及它 returns 代表的承诺。

服务工作者规范中记录了确定的步骤集,从“Start Register”作业开始。根据 service worker scriptURLscope 之前是否注册过,行为会略有不同,所以首先要问自己的是,你是否只在注册时才看到这个第一次,在“干净”的浏览器中,或者当有相同的 pre-existing service worker 注册时你是否也看到它。

无论如何,navigator.serviceWorker.register() returns 的承诺一旦明确注册是针对有效的脚本资源 JavaScript 且 scope 后跟安全限制。

如果您尝试使用无效语法注册 JavaScript 文件,或者如果您使用与 service worker 限制不兼容的 scope,或者如果有网络,承诺将被拒绝阻止请求 JavaScript 的错误。

重要的是要注意 navigator.serviceWorker.register() 解析的方式并不能反映被注册的 service worker 是否实际安装正确。可以注册一个不包含任何语法错误且具有有效范围的服务工作者脚本(因此 navigator.serviceWorker.register() 将满足),但在 install 阶段变为 redundant ,也许因为它传递给 installEvent.waitUntil() 的承诺最终被拒绝了。 navigator.serviceWorker.register() 满足的事实确实意味着您将始终拥有一个 installed 服务人员!

独立于注册使用navigator.serviceWorker.ready

那么,让我们开始讨论您的特定代码片段。如果您看到 navigator.serviceWorker.register() 返回的承诺在您的网络连接不稳定时经过长时间的延迟后被拒绝,那么它几乎肯定会被拒绝,因为对服务工作者脚本的网络请求最终超时。对于这种情况,您实际上无能为力——这是当您的网络不稳定时发生的情况的定义,并确保您的服务工作者 always "fresh",对服务工作者脚本的请求将绕过任何缓存默认。

我认为你 运行 遇到的问题是,一旦有 active service worker,你正在使用 navigator.serviceWorker.ready 执行一些代码,但是你仅在 navigator.serviceWorker.register() 承诺实现时调用它。出于上述原因,我认为您不应该以这种方式构建代码。

相反,我会将您的代码分成两步 - 一个调用 navigator.serviceWorker.register(),另一个等待 navigator.serviceWorker.ready 完成,彼此独立。

navigator.serviceWorker.register()
  .then(() => console.log('Registration succeeded.')
  .catch((error) => console.log('Registration failed: ', error));

navigator.serviceWorker.ready.then(() => {
  // Put whatever code requires an active service worker here.
});