11

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

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

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} 

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

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

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

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

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

Да, это [похоже, цикл событий не останавливается] (http://stackoverflow.com/a/2734311/1048572) во время «предупреждения» :-) – Bergi

+0

Берги, спасибо за ответ. Однако вся идея веб-работников заключается в том, что они работают в фоновом потоке и не мешают пользовательскому интерфейсу (bobince даже упоминает это оговорку в ответе, на который вы ссылались). И это, очевидно, в какой-то мере: приведенные выше примеры показывают, что в какой-то степени они выполняются, даже когда основной поток блокируется. Мой большой вопрос в том, где линия находится между тем, что есть и не заблокировано основным потоком. – heliotrope

+0

«Неблокирование не обязательно означает одновременное». Исходная статья: http://code.tutsplus.com/tutorials/getting-started-with-web-workers--net-27667 – sergolius

ответ

4

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

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

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

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

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

+1

Welp, вот что. В моем тестировании такая же проблема возникает и с WebSockets (даже если соединение уже установлено, сообщения не могут передаваться взад и вперед, пока основной поток заблокирован). В целом, есть ли у вас какое-либо представление о том, почему сетевые вызовы не отправляются, пока поток пользовательского интерфейса заблокирован? Совершенно разумно, если нет, просто любопытно ... – heliotrope

+1

Боюсь, я не могу дать вам точного обоснования, почему он работает так, как он делает, или если он даже преднамерен. В любом случае, чтобы решить вашу проблему, рассмотрели ли вы использование lightbox modal и/или ['Notification's] (https://developer.mozilla.org/en-US/docs/Web/API/notification) вместо' alert() '? – josh3736

+1

А, так оно и есть (было просто любопытно). Да, я подумал об этом, но, к сожалению, в соответствующем приложении есть несколько случаев, когда я не могу избежать предупреждения блокировки (например, подключаюсь к событиям «pageunload»), и я также просто стараюсь максимально повысить устойчивость к непредвиденная блокировка: /. Большое спасибо за Ваш ответ! – heliotrope

2

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

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

+1

Вопрос только что обновлен до * Исправлено *. Надеюсь, скоро это будет выпущено. – tvbusy

+0

Он исправлен в Chrome 62 (доступный как Chrome Dev на данный момент) – Erik