Работник службы задерживает несколько минут перед отправкой события controllerchange - PullRequest
0 голосов
/ 18 октября 2018

Я перевожу наш PWA с установки appcache на сервисных работников.Пусть это работает, за исключением того, что обновления иногда занимают более 10 минут, прежде чем уведомить клиента через событие controllerchange.Иногда это почти мгновенно.Я понятия не имею, что это за переменная.

let swRegWorker = null;

function showUpdateBar() {
    let snackbar = document.getElementById('snackbar');
    snackbar.className = 'show'
}

// the click event on the pop up notification
document.getElementById('reload').addEventListener('click', function() {
    window.location.reload()
});

if('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/swmobile.js').then(reg => {
        swRegWorker = reg;

        // check for updates every 1 minutes
        setInterval(function(){
            swRegWorker.update()
        }, 1*60000)
    });

    // Listen for claiming of our ServiceWorker
    navigator.serviceWorker.addEventListener('controllerchange', function(event) {
      console.log('cchange', event);
      showUpdateBar()
    });
}
    #snackbar {
        visibility: hidden;
        min-width: 250px;
        margin-left: -125px;
        background-color: #333;
        color: #fff;
        text-align: center;
        border-radius: 2px;
        padding: 16px;
        position: fixed;
        z-index: 1;
        left: 50%;
        bottom: 30px;
    }

    #snackbar.show {
        visibility: visible;
        -webkit-animation: fadein 0.5s;
        animation: fadein 0.5s;
    }

    #snackbar #reload {
        font-weight: bold;
    }

    @-webkit-keyframes fadein {
        from {
            bottom: 0;
            opacity: 0;
        }
        to {
            bottom: 30px;
            opacity: 1;
        }
    }

    @keyframes fadein {
        from {
            bottom: 0;
            opacity: 0;
        }
        to {
            bottom: 30px;
            opacity: 1;
        }
    }
<div>0855</div>

<div id="snackbar">A new version of this app is available.<br/>Click <a id="reload">HERE</a> to update.</div>

var CACHE_NAME = 'app-0855';

var urlsToCache = [
    '/',
    '/mobile.html',
    '/manifest.json'
];

self.addEventListener('install',function(e) {
    caches.open(CACHE_NAME).then(function(cache) {
        console.log('updating cache to', CACHE_NAME);
        return cache.addAll(urlsToCache)
    })
    .then(function() {
        self.skipWaiting()
    })
});

self.addEventListener('activate', function(e) {
    console.log('activate');

    e.waitUntil(
        Promise.all([
            self.clients.claim(),
            caches.keys().then(function(cacheNames) {
                return Promise.all(
                    cacheNames.map(function(cacheName) {
                        if(cacheName !== CACHE_NAME) {
                            console.log('deleting', cacheName);
                            return caches.delete(cacheName);
                        }
                    })
                )
            })
        ])
    )
});

self.addEventListener('fetch', function(event) {
    event.respondWith(
        caches.match(event.request)
        .then(function(response) {
            if(response){
                return response
            }
            // not in cache, return from network
            return fetch(event.request)
        })
    )
});

Таким образом, приложение изначально устанавливается нормально и сразу после загрузки.Однако, когда я изменяю версию html и кеша в js-файле сервисного работника, в следующую минуту я вижу, как он загружает и устанавливает обновление, и временно вижу оба кеша в Chrome devtool.Затем старый кэш успешно удаляется.

В идеальных случаях клиент немедленно выскакивает сообщение о доступности обновления.Но примерно в 50% случаев, по неизвестной причине, может пройти 5-30 минут, прежде чем клиент увидит всплывающее окно обновления для обновления и запуска новой версии.

Журналы консоли показывают, что все работает вработник службы, но вызов self.clients.claim, кажется, ждет некоторое количество неопределенного времени, прежде чем запустить событие controllerchange для клиента.

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

1 Ответ

0 голосов
/ 18 октября 2018

Мне кажется, я решил это.У меня была небольшая тестовая страница, которая всегда работала, как говорит дизайн спецификации, но в нашем «реальном» приложении эта загадочная задержка продолжала появляться.

Как оказалось, это наше использование SSE (события, отправленные сервером)или EventSource), который, казалось, «блокировал» активный экземпляр работника службы.

Я добавил следующий код в обработчик выборки, и все задержки, похоже, прошли!

    const request = event.request;
    const acceptHeader = request.headers.get('Accept');
    const url = request.url;

    if(acceptHeader.includes('text/event-stream') || url.indexOf('events') !== -1 || url.indexOf('api') !== -1) {
        console.log('ignoring', url);
        return
    }

Хотя я считаю, что вышеуказанное решило 99% проблемы, у меня все еще был один экземпляр на моем телефоне, где уведомление о смене контроллера никогда не происходило.Поэтому в качестве меры предосторожности я также добавил это в файл рабочего сервиса и в клиенте, который кажется избыточным, но безвредным и дает 100% успешное уведомление об обновлении.

function send_message_to_client(client, msg) {
    return new Promise(function(resolve, reject){
        var msg_chan = new MessageChannel();

        msg_chan.port1.onmessage = function(event){
            if(event.data.error){
                reject(event.data.error);
            }
            else{
                resolve(event.data);
            }
        };

        client.postMessage("SW says: '"+msg+"'", [msg_chan.port2]);
    });
}


function send_message_to_all_clients(msg) {
    clients.matchAll().then(clients => {
        clients.forEach(client => {
            send_message_to_client(client, msg).then(m => console.log("SW Received Message: "+m));
        })
    })
}


self.addEventListener('activate', function(e) {
    console.log('activate');
    send_message_to_all_clients('Upgrade ready!') // the NEW line

    e.waitUntil(
        Promise.all([
            self.clients.claim(),
            caches.keys().then(function(cacheNames) {
                return Promise.all(
                    cacheNames.map(function(cacheName) {
                        if(cacheName !== CACHE_NAME) {
                            console.log('deleting', cacheName);
                            return caches.delete(cacheName);
                        }
                    })
                )
            })
        ])
        .then(function() {
            console.log('promises kept')
        })
        .catch(function(err) {
            console.log('promises broken', err)
        })
    )
});
    // handler for messages coming from the service worker
    navigator.serviceWorker.addEventListener('message', function(event) {
        console.log("client received: " + event.data);
        showUpdateBar()
    });
...