Веб-работник заблокирован основным потоком в 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). Конкретно, я хотел бы знать (если кто-нибудь знает):
- какая рабочая активность блокируется основным потоком в Chrome?
- есть ли способ обойти это? Я бы очень хотел иметь возможность установить соединение WebSocket в работнике, а затем продолжать пинг/понг туда и обратно, даже если что-то (например, предупреждение/подтверждение) блокирует основной поток.
- это все бред, и я просто делаю что-то глупое?
3 ответов
ваше наблюдение верно. Когда поток пользовательского интерфейса заблокирован, сетевые вызовы не отправляются.
еще хуже, Chrome имеет лучшее поведение группы. Когда работник делает запрос XHR, когда поток пользовательского интерфейса заблокирован:
- Chrome: все запросы поставлены в очередь. Браузер фактически не будет выдавать запросы, пока поток пользовательского интерфейса не разблокируется. С положительной стороны, рабочий поток по-прежнему свободен для запуска.
- Firefox:
new XMLHttpRequest()
блоки до пользовательского интерфейса нить разблокируется. - IE:
xhr.open()
блокирует, пока поток пользовательского интерфейса не разблокируется.
хотя Chrome, к счастью, не заставляет рабочий поток останавливаться и ждать (даже если он не получит никаких данных), Firefox и IE заставят рабочий поток ждать потока пользовательского интерфейса при попытке сделать запрос XHR.
нет никакого способа обойти это, вы обязаны браузер отправлять запросы от вашего имени. Я не проводил никаких тестов с WebSockets, но они мая доставить событий, даже если UI-поток блокируется. В худшем случае полученные сообщения будут стоять в очереди, пока поток пользовательского интерфейса не разблокируется.
в случае, если кто-то наткнется на это, это поведение подтверждается как ошибка (к свободному определению "ошибка " как" не ведет себя так, как должно") в Blink, по состоянию на февраль 2015 года:
У меня такая же проблема с веб-работником, который выполняет своего рода keep-alive: он периодически пингует сервер, чтобы сообщить, что страница все еще жива. Я также использую консоль.войти в работник, но я уверен, что это не причина.
после некоторых исследований я могу заявить, что проблема может решить две разные ситуации:
- длительная операция пользовательского интерфейса блокирует основной поток, и запросы работника не выполняются. Это случай образца иллюстрируется гелиотропом. По состоянию на сентябрь 2018 года проблема возникает только в Firefox и IE, так как Chrome и Edge могут корректно обрабатывать действия на рабочем, когда основной поток заблокирован.
чтобы решить эту проблему, я думаю отправить специальный пинг на сервер перед началом любой долгосрочной операции, чтобы сообщить, что он ничего не получит от меня, пока операция не будет завершена.
- 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 (