服务工作者不会跳过等待状态 [英] service worker doesn't skip waiting state

查看:151
本文介绍了服务工作者不会跳过等待状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我还在做实验以掌握 Service Worker,我面临一个问题,可能是因为我缺乏 JavaScript 和 Service Worker 的专业知识.

当我希望新的 service worker 使用 postMessage()skipWaiting() 时,问题就会发生.如果我显示一个带有按钮的弹出窗口,并将调用绑定到 postMessage() 那里,一切正常.如果我直接调用 postMessage() ,它不起作用.这是一个竞争条件,因为有时它有效,但我无法确定竞争条件.

顺便说一句,postMessage() 调用 WORKS,服务工作者在获取消息时记录它应该做的事情:

//监听来自客户端的消息.self.addEventListener('message', event => {开关(事件.数据){case 'skipWaiting': self.skipWaiting();console.log('我跳过了等待......额外');休息;}});

这是代码.重要的一点是 if (registration.waiting) 条件.未注释的代码有效,注释的无效:

//注册服务工作者.如果(导航器中的'serviceWorker'){//显示和隐藏更新 toast 的助手.让 hideUpdateToast = () =>{document.getElementById('update_available').style.visibility = '隐藏';};让 showUpdateToast = (serviceworker) =>{document.getElementById('update_available').style.visibility = 'visible';document.getElementById('force_install').onclick = () =>{serviceworker.postMessage('skipWaiting');hideUpdateToast();};document.getElementById('close').onclick = () =>hideUpdateToast();};window.addEventListener('load', () => {让刷新 = false;navigator.serviceWorker.addEventListener('controllerchange', () => {如果(令人耳目一新)返回;刷新 = 真;window.location.reload();});navigator.serviceWorker.register('/sw.js').then(registration => {//获取了一个新的 service worker,注意状态变化.////每次获取 Service Worker 时都会触发此事件,并且//成功解析并进入安装"状态.这//也发生在第一次访问页面时//第一次为这个页面获取服务工作者,当//页面没有控制器,但在这种情况下没有新的//版本可用且通知不得出现.////因此,如果页面没有控制器,则不会显示通知.registration.addEventListener('updatefound', () => {//返回;//整我registration.installing.onstatechange = function () {//没有箭头函数,因为需要 'this'.if (this.state == '已安装') {如果(!navigator.serviceWorker.controller){console.log('此服务工作者的首次安装.');} 别的 {console.log('新的 Service Worker 已准备好激活.');showUpdateToast(this);}}};});//如果 Service Worker 处于等待"状态,则可能是用户//当 Service Worker 在//'安装' 状态或者'updatefound' 事件被触发//在它可以被监听之前,或者类似的东西.总之,在//在这种情况下,必须再次显示通知.//如果(注册.等待){console.log('新的 service worker 正在等待.');//showUpdateToast(registration.waiting);//以上工作,但这不起作用.registration.waiting.postMessage('skipWaiting');}}).catch(错误=>{console.log('Service Worker 注册失败!');控制台日志(错误);});});}

为什么使用按钮onclick 事件的间接调用有效,而调用postMessage() 却无效?

我完全不知所措,我敢打赌答案很简单,只是我太瞎了,看不到.

非常感谢.

解决方案

看起来像是 Chromium 或 WebKit 中的错误,因为此代码在 Firefox 中一直有效,但在 Chrome 和 Edge 中大部分时间都失败.

我已向 Chromium 报告了该错误,让我们看看是错误还是我的代码很奇怪.我设法构建了尽可能小的代码,但仍然会重现该问题,它非常小,可以在错误报告中找到.

可以在此处找到该报告.>

抱歉打扰了,我会继续调查这个问题,但无论我如何尝试,代码仍然不时失败,我无法在我的代码中发现竞争条件.

I'm still doing experiments in order to master service workers, and I'm facing a problem, probably because of my lack of expertise in JavaScript and service workers.

The problem happens when I want the new service worker to skipWaiting() using postMessage(). If I show a popup with a button and I bind a call to postMessage() there, everything works. If I call postMessage() directly, it doesn't work. It's a race condition because SOMETIMES it works, but I can't identify the race condition.

BTW, the postMessage() call WORKS, the service worker is logging what it should when getting the message:

// Listen to messages from clients.
self.addEventListener('message', event => {
    switch(event.data) {
        case 'skipWaiting': self.skipWaiting(); console.log('I skipped waiting... EXTRA');
            break;
    }
}); 

Here is the code. The important bit is on the if (registration.waiting) conditional. The uncommented code works, the commented one doesn't:

// Register service worker.
if ('serviceWorker' in navigator) {
    // Helpers to show and hide the update toast.
    let hideUpdateToast = () => {
        document.getElementById('update_available').style.visibility = 'hidden';
    };

    let showUpdateToast = (serviceworker) => {
        document.getElementById('update_available').style.visibility = 'visible';
        document.getElementById('force_install').onclick = () => {
            serviceworker.postMessage('skipWaiting');
            hideUpdateToast();
        };
        document.getElementById('close').onclick = () => hideUpdateToast();
    };

    window.addEventListener('load', () => {

        let refreshing = false;
        navigator.serviceWorker.addEventListener('controllerchange', () => {
            if (refreshing) return;
            refreshing = true;
            window.location.reload();
        });

        navigator.serviceWorker.register('/sw.js').then(registration => {
            // A new service worker has been fetched, watch for state changes.
            //
            // This event is fired EVERY TIME a service worker is fetched and
            // succesfully parsed and goes into 'installing' state. This
            // happens, too, the very first time the page is visited, the very
            // first time a service worker is fetched for this page, when the
            // page doesn't have a controller, but in that case there's no new
            // version available and the notification must not appear.
            //
            // So, if the page doesn't have a controller, no notification shown.
            registration.addEventListener('updatefound', () => {
                // return;  // FIXME
                registration.installing.onstatechange = function () {  // No arrow function because 'this' is needed.
                    if (this.state == 'installed') {
                        if (!navigator.serviceWorker.controller) {
                            console.log('First install for this service worker.');
                        } else {
                            console.log('New service worker is ready to activate.');
                            showUpdateToast(this);
                        }
                    }
                };
            });

            // If a service worker is in 'waiting' state, then maybe the user
            // dismissed the notification when the service worker was in the
            // 'installing' state or maybe the 'updatefound' event was fired
            // before it could be listened, or something like that. Anyway, in
            // that case the notification has to be shown again.
            //
            if (registration.waiting) {
                console.log('New service worker is waiting.');
                // showUpdateToast(registration.waiting);

                // The above works, but this DOESN'T WORK.
                registration.waiting.postMessage('skipWaiting');
            }

        }).catch(error => {
            console.log('Service worker registration failed!');
            console.log(error);
        });
    });
}

Why does the indirect call using a button onclick event works, but calling postMessage() doesn't?

I'm absolutely at a loss and I bet the answer is simple and I'm just too blind to see it.

Thanks a lot in advance.

解决方案

Looks like a bug in Chromium or WebKit, because this code works all the time in Firefox but fails in Chrome and Edge most of the time.

I've reported that bug to Chromium, let's see if it is a bug or my code is weird. I've managed to build the smallest code possible that still reproduces the issue, it's quite small and it can be found on the bug report.

The report can be found here.

Sorry for the noise, I'll keep investigating the issue but no matter how I tried, the code still fails from time to time, I can't spot the race condition on my code.

这篇关于服务工作者不会跳过等待状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆