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

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

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

base.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);

worker.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}

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

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

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

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

  • Какая активность рабочего заблокирована основным потоком в Chrome?
  • Есть ли способ обойти это? Мне очень хотелось бы иметь возможность установить соединение WebSocket у Рабочего, а затем продолжить PING/PONG взад и вперед, даже если что-то (например, предупреждение/подтверждение) блокирует основной поток.
  • Это все глупости, и я просто делаю что-то глупое?

Ответ 1

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

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

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

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

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

Ответ 2

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

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