Веб-работник заблокирован основным потоком в Chrome

у меня есть Веб-Работника. Я хочу периодически обращаться к нему с сетевыми запросами. Одна вещь, которую я особенно хочу, - сделать эти запросы, даже если основной поток выполнения JS заблокирован (например, окном.тревога.) Я использую Chrome 38.

однако, когда я пытаюсь сделать сетевые запросы в рабочем, запросы, похоже, блокируются потоком пользовательского интерфейса. Вот надуманный пример, иллюстрирующий проблема:

базы.js:

var worker = new Worker("/worker.js");

setTimeout(function() {
    console.log("begin blocking");
    var startDt = new Date();
    var blockPeriod = 5000;
    var a;
    // Obviously we'd never actually do this, but this while loop
    // is a convenient way to create the problem case (a blocked main
    // thread).
    while ((new Date() - startDt) < blockPeriod) {
        a = 0;
    }
    console.log("stop blocking");
}, 3000);

работника.js:

var requestInterval = 1000;

var sendRequest = function() {
    console.log("Send request interval");

    var request = new XMLHttpRequest();
    request.open("GET", "/ping", true);

    request.onload = function() {
        if (request.status === 200){
            console.log(request.responseText)
        } else {
            console.log(request.status)
        }
    };

    request.onerror = function() {
        console.log("error")
    };

    request.send();

    setTimeout(sendRequest, requestInterval);
}

sendRequest();

результат, который я вижу, заключается в том, что мы видим успешные HTTP-запросы в течение трех секунд, пока не начнется блокировка. На данный момент мы не видим ничего зарегистрированного на консоли до конца блокировки, и в этот момент мы видим пять "интервалов отправки запроса", за которыми следуют 5 журналов ответа, например:

Send request interval
{"pong": true}
Send request interval 
{"pong": true} 
Send request interval
{"pong": true}
Send request interval
{"pong": true}
begin blocking
stop blocking
5x Send request interval
5x {"pong": true}
Send request interval
{"pong": true}

Я также вижу в своих журналах сервера, что никакие запросы не выполняются в это время блокировки, а затем все эти пять запросов поступают примерно одновременно в конце периода блокирования.

учитывая, что" интервал отправки запроса " происходит пять раз подряд, работник, очевидно, продолжает выполнять: если бы это было не так, он не смог бы пройти в очередь на следующую итерацию. Я также обнаружил, что если я блокирую, вызывая окно.предупреждение вместо того, чтобы вращаться в цикле, я получаю сообщения журнала с начала sendRequest интервалом в 1 секунду, а затем получить ответ обработчик регистрирует сообщения в большом пакете, как только я прекращаю блокировку.

в Firefox фоновый поток, похоже, полностью останавливается в этом случае (я не получаю тот же пакет из пяти запросов в очереди в течение заблокированного периода). Однако в этом случае я ориентируюсь только на Chrome (и я в конечном итоге хочу использовать WebSockets, которые даже не работают в Firefox), поэтому меня это не интересует.

все вместе, это приводит меня к мысли, что есть некоторые классы активности в веб-рабочих, которые блокируются нерестовым потоком, а некоторые-нет (я изначально видел такое же поведение с WebSockets). Конкретно, я хотел бы знать (если кто-нибудь знает):

  1. какая рабочая активность блокируется основным потоком в Chrome?
  2. есть ли способ обойти это? Я бы очень хотел иметь возможность установить соединение WebSocket в работнике, а затем продолжать пинг/понг туда и обратно, даже если что-то (например, предупреждение/подтверждение) блокирует основной поток.
  3. это все бред, и я просто делаю что-то глупое?

3 ответов


ваше наблюдение верно. Когда поток пользовательского интерфейса заблокирован, сетевые вызовы не отправляются.

еще хуже, Chrome имеет лучшее поведение группы. Когда работник делает запрос XHR, когда поток пользовательского интерфейса заблокирован:

  • Chrome: все запросы поставлены в очередь. Браузер фактически не будет выдавать запросы, пока поток пользовательского интерфейса не разблокируется. С положительной стороны, рабочий поток по-прежнему свободен для запуска.
  • Firefox:new XMLHttpRequest() блоки до пользовательского интерфейса нить разблокируется.
  • IE:xhr.open() блокирует, пока поток пользовательского интерфейса не разблокируется.

хотя Chrome, к счастью, не заставляет рабочий поток останавливаться и ждать (даже если он не получит никаких данных), Firefox и IE заставят рабочий поток ждать потока пользовательского интерфейса при попытке сделать запрос XHR.

нет никакого способа обойти это, вы обязаны браузер отправлять запросы от вашего имени. Я не проводил никаких тестов с WebSockets, но они мая доставить событий, даже если UI-поток блокируется. В худшем случае полученные сообщения будут стоять в очереди, пока поток пользовательского интерфейса не разблокируется.


в случае, если кто-то наткнется на это, это поведение подтверждается как ошибка (к свободному определению "ошибка " как" не ведет себя так, как должно") в Blink, по состоянию на февраль 2015 года:

https://code.google.com/p/chromium/issues/detail?id=443374


У меня такая же проблема с веб-работником, который выполняет своего рода keep-alive: он периодически пингует сервер, чтобы сообщить, что страница все еще жива. Я также использую консоль.войти в работник, но я уверен, что это не причина.

после некоторых исследований я могу заявить, что проблема может решить две разные ситуации:

  1. длительная операция пользовательского интерфейса блокирует основной поток, и запросы работника не выполняются. Это случай образца иллюстрируется гелиотропом. По состоянию на сентябрь 2018 года проблема возникает только в Firefox и IE, так как Chrome и Edge могут корректно обрабатывать действия на рабочем, когда основной поток заблокирован.

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

  1. a долгосрочный асинхронный ajax вызов выполняется основным потоком, и запросы работника помещаются в очередь. Некоторые настройки на сервере предотвращают параллельное выполнение нескольких вызовов ajax: другие запросы помещаются в очередь до завершения первого вызова ajax. Проблема специфична для сервера, поэтому она не зависит от какого-либо браузера в частности.

эта проблема, в моем случае, из-за ASP.NET блокировка состояния сеанса: ASP.NET конвейер не будет обрабатывать запросы, принадлежащие одному сеансу одновременно но выстраивает их в очередь и выполняет последовательно. Вот подробная ссылка: http://tech-journals.com/jonow/2011/10/22/the-downsides-of-asp-net-session-state.

Маркировка состояния сеанса контроллера как ReadOnly решит проблему, полностью отключив состояние сеанса в Интернете.config () серьезно повысит производительность всего приложения.