сервисный работник не пропускает состояние ожидания

Я все еще провожу эксперименты, чтобы освоить сервис-воркеры, и столкнулся с проблемой, вероятно, из-за отсутствия у меня опыта в JavaScript и сервис-воркерах.

Проблема возникает, когда я хочу, чтобы новый сервисный работник skipWaiting() использовал postMessage(). Если я показываю попап с кнопкой и привязываю туда вызов postMessage(), то все работает. Если я вызову postMessage() напрямую, это не сработает. Это состояние гонки, потому что ИНОГДА оно работает, но я не могу определить состояние гонки.

Кстати, вызов postMessage() РАБОТАЕТ, сервисный работник регистрирует то, что должен, при получении сообщения:

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

Вот код. Важный бит находится в условном выражении if (registration.waiting). Незакомментированный код работает, а закомментированный нет:

// 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);
        });
    });
}

Почему непрямой вызов с помощью события кнопки onclick работает, а вызов postMessage() — нет?

Я совершенно в растерянности, и держу пари, что ответ прост, и я слишком слеп, чтобы увидеть его.

Заранее большое спасибо.


person Raúl Núñez de Arenas Coronado    schedule 03.02.2021    source источник
comment
Ясно, что это состояние гонки, закомментированный код отлично работает, если я добавлю туда тайм-аут...   -  person Raúl Núñez de Arenas Coronado    schedule 03.02.2021
comment
developers.google.com/web/tools/workbox/guides/advanced- рецепты работают из коробки. Если вы не используете workbox, подумайте об этом (пора изучить его, но очень хорошо охватывает кэширование и регистрацию ПО, обновления, условия гонки.)   -  person Robert Rowntree    schedule 03.02.2021
comment
Спасибо, Роберт, но ключ здесь в том, чтобы понять, почему все это происходит :) Я знаю, что могу использовать Workbox, но я хочу как можно глубже изучить всю эту тему сервис-воркеров и понять, почему что-то происходит. В продакшне я могу использовать Workbox, мне так проще. Кроме того, я не хочу использовать баннер или другой элемент пользовательского интерфейса. Это уже работает как шарм с моим кодом, я хочу знать, почему другой вариант, прямой маршрут, не работает...   -  person Raúl Núñez de Arenas Coronado    schedule 03.02.2021
comment
github.com/GoogleChrome/ workbox/blob/ - та же проблема, о которой вы упоминали в предыдущей версии WB.. может быть вам полезна   -  person Robert Rowntree    schedule 04.02.2021
comment
Большое спасибо за помощь, Роберт, но это не похоже на мою проблему. Может быть, я не нахожу проблему, но те, которые я проверил, все идут в одном направлении: какой-то сервисный работник заблокирован SSE или соединением через веб-сокет, но это не мой случай, я проверял. Кстати, это происходит только в webkit, все другие браузеры, которые я использовал, работают отлично, так что, возможно, это проблема webkit, я не знаю...   -  person Raúl Núñez de Arenas Coronado    schedule 04.02.2021
comment
далеко в сорняках, но стоит прочитать Посника здесь: github.com/GoogleChrome/workbox/ вопросы/   -  person Robert Rowntree    schedule 04.02.2021
comment
Извини, Роберт, но я не понимаю. Какое отношение этот комментарий имеет к проблеме, на которую я указывал? Я имею в виду, что основная проблема, с которой я сталкиваюсь, заключается в том, что обещание, возвращаемое skipWaiting(), не разрешается, функция по какой-то причине блокируется в webkit, если я не добавлю задержку, в то время как сообщение, на которое вы ссылаетесь, говорит о чем-то, что я уже знаю, вы можно наблюдать за событиями в обоих сервис-воркерах, и вы получите оба изменения состояния... В любом случае, спасибо, потому что источники Workbox довольно информативны, хотя это и не решает мою проблему   -  person Raúl Núñez de Arenas Coronado    schedule 04.02.2021
comment
Не могли бы вы объяснить, как вы решаете эту проблему с тайм-аутом? У меня та же проблема, и мне нужно пропустить ожидание, чтобы убедиться, что новая версия доступна для всех моих пользователей как можно скорее, и эта проблема создает для нас много проблем!   -  person Mehrnoosh    schedule 16.07.2021
comment
Извините за поздний ответ, Мернуш. Задержка/тайм-аут добавлены в приведенном выше коде в сервис-воркере. Вместо прямого вызова self.skipWaiting() сделайте это как setTimeout(self.skipWaiting, 100). Возможно, вам придется настроить тайм-аут, и это хак, который мне не нравится, но он сработал. Сейчас я этим не пользуюсь, я просто вызываю self.skipWaiting() непосредственно в обработчике install сервис-воркера. Брутально, но это работает для меня и до поры до времени.   -  person Raúl Núñez de Arenas Coronado    schedule 21.07.2021


Ответы (1)


Похоже на ошибку в Chromium или WebKit, потому что этот код все время работает в Firefox, но большую часть времени дает сбой в Chrome и Edge.

Я сообщил об этой ошибке в Chromium, давайте посмотрим, ошибка ли это или мой код странный. Мне удалось создать наименьший возможный код, который все еще воспроизводит проблему, он довольно мал, и его можно найти в отчете об ошибке.

Отчет можно найти здесь.

Извините за шум, я продолжу исследовать проблему, но как бы я ни пытался, код все еще время от времени дает сбой, я не могу определить состояние гонки в своем коде.

person Raúl Núñez de Arenas Coronado    schedule 08.02.2021
comment
Пожалуйста, обновите, если вы нашли ответ на этот вопрос. Я сталкиваюсь с той же проблемой. - person Craig Howell; 29.04.2021
comment
К сожалению, как видно из отчета, ошибка еще даже не проверена. Что я сделал в своем PWA, так это агрессивно взял под свой контроль страницу всякий раз, когда можно установить новый сервис-воркер. Но это роскошь, которую я могу себе позволить в МОЕМ PWA, чего не могут сделать другие PWA... Если когда-либо появится решение (это Google, не известный исправлением ошибок), я обновлю свой ответ. - person Raúl Núñez de Arenas Coronado; 30.04.2021