2016-05-08 7 views
7

Я инициировал веб-рабочего на хроме, и у него была простая функция, которая вызывалась многократно, используя setTimeout. Удивительно, что веб-рабочий закончил работу после того, как функция была вызвана около 1000 раз. Может ли кто-нибудь объяснить, почему? Я думаю, что хром делает некоторую оптимизацию.Работники веб-сайта заканчиваются внезапно

webworker.js

function hi() { 
    postMessage('1'); 
    setTimeout(hi, 1); 
} 
hi(); 

main.js

var blob = new Blob([code]); 
var blobURL = window.URL.createObjectURL(blob); 
var worker = new Worker(blobURL); 
worker.onmessage = function(data) { 
    console.log(data.data); // gets called around 1000 times and done 
}; 

EDIT: Воспроизводится в скрипкой: http://jsfiddle.net/meovfpv3/1/ Это, кажется, принимает сколь угодно долго для onmessage ca llback, чтобы остановить стрельбу, так же быстро, как несколько секунд и до +5 минут

+0

Конечно, мы можем объяснить, почему, но не просим нас угадать. Вам нужно будет показать ** минимальный проверяемый пример **, чтобы мы могли проверить его. –

+0

@ IsmaelMiguel Я обновил вопрос с помощью кода worker.js. В основном я делаю blob из строки и отдаю ее работнику –

+0

У меня такая же проблема. Я обнаружил, что Рабочие не заканчиваются (тестируются с помощью 'console.log'), обратный вызов onmessage просто перестает быть запущенным в какой-то момент. Очень странное и неприемлемое поведение браузера! * table flip * –

ответ

1

Вы пытаетесь отправить сообщение каждые 1 мс? Тогда вы, вероятно, имел в виду использовать setInterval():

setInterval(function(){ 
    postMessage('1'); 
}, 1); 

Edit: я неправильно видел рекурсию, которая не была там, просто потому, что я искал его. Я все равно использовал бы setInterval более setTimeout.

+1

В чем разница ? 'hi()' is ** не ** вызывает себя рекурсивно. Он вызывает себя в обратном вызове, что совсем другое. –

+0

Нет никакой разницы (за исключением, возможно, разницы в производительности, которая не имеет значения). –

+0

@torazaburo Ну, это смущающая ошибка, которую я сделал! Наверное, главное отличие - читаемость ?! – Lesley

5

Вот мое лучшее предположение о том, что происходит. Публикуя сообщение от Web Worker каждые 1 мс, вы требуете, чтобы основной поток обрабатывал каждое отправленное сообщение в течение 1 мс.

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

Теперь, поскольку вы отправляете сообщения от веб-рабочего быстрее, чем их можно обработать, эта очередь необработанных сообщений будет становиться все больше и больше. В какой-то момент Chrome собирается взломать руки и сказать: «В очереди слишком много сообщений», и вместо очередей новых сообщений для обработки он их отбрасывает.

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


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

http://jsfiddle.net/meovfpv3/3/

Вы можете видеть, что при разумном SetTimeout 100 мс, все сообщения имеют достаточно времени, чтобы обработать до появления следующего сообщения.

Когда вы опускаете setTimeout до 1 мс, цепочка сообщений не успевает закончить до того, как будет отправлено следующее сообщение, и счетчики в каждом потоке в конечном итоге будут рассортированы, отключив пункт 10 и прекратив работу веб-рабочего.


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


Для полноты здесь есть копия JSFiddle code:

работник:

var counter2 = 0; 
    var rcvd = true; 
    function hi() { 
    counter2++; 
    console.log("") 
    console.log("postMessage", counter2) 
    postMessage(counter2); 
    if (!rcvd) { 
     self.close(); 
     console.log("No message received"); 
    } 
    rcvd = false; 
    setTimeout(hi, 1); 
    } 
    hi(); 
    onmessage = function(e) { 
    rcvd = true; 
    console.log("secondMessage", e.data); 
    } 

Главная:

var ww = document.querySelector('script[type="text/ww"]'), 
    code = ww.textContent, 
    blob = new Blob([code], {type: 'text/javascript'}), 
    blobUrl = URL.createObjectURL(blob), 
    worker = new Worker(blobUrl), 
    counter = 0; 

worker.onmessage = function(e) { 
    counter++; 
    console.log("onmessage:", counter); 
    worker.postMessage(e.data); 
} 
+0

Но setTimeout фактически не будет исполнять каждую миллисекунду; в лучшем случае он будет выполняться каждые 10-20 мс. –

+0

@torazaburo Это правильно, но 'setTimeout' будет выполняться так быстро, как возможно, что быстрее, чем сообщения, которые он создает, могут быть обработаны. –

+0

Спасибо Макс, интересная теория. Однако мой исходный код (который отличается от OP) использует тайм-аут в 85 мс, поэтому он _seems_, чтобы развенчать теорию. Я даже не уверен, что оригинальный jsfiddle, который я добавил, оправдывает его, поскольку проблема, похоже, достоверно воспроизводится. К сожалению, я не могу предоставить весь свой прецедент в скрипке, поскольку есть конфиденциальный код. Похоже, вы все равно получите награду (A для усилий!), Если только кто-то еще не может указать на проблему. Я подозреваю, что проблема связана с Chrome v50.2xx и что-то связано с оптимизацией Web Worker. –

4

Во-первых, несколько наблюдений, которые я не могу объяснить, но являются интересными и могут быть вдохновляющими для кого-то:

  • @Anson - Если я ставлю свой код jsFiddle в Codepen (еще в Chrome) нет никаких проблем. Обратный вызов onmessage просто продолжает работать!

  • И еще в jsFiddle ... Он терпит неудачу, даже изменяя setTimeout к длинной щели, как 10s, так что это не столько раз, что посты рабочих сообщений, то, как долго до onmessage обратного вызова прекращается стрельба - которая имеет много различий.

Тогда я нашел несколько способов, чтобы сохранить onmessage обработчик живым в этом конкретном примере:

  • добавить кнопку/ссылку в HTML и обработчик (я использовал JQuery), что будет прекратить работника по щелчку. Просто добавление этого кода исправляет его. $("#stop").on("click",function(e){e.preventDefault();worker.terminate();});
  • Просто добавьте console.log(worker) после определения onmessage.
  • Вдохновленный ответом, размещенным в related question, вы также можете просто добавить window.worker = worker после определения onmessage.

Что-то об упоминании worker снова во всех случаях, кажется, поддерживает его.

+0

Конечно, это сработает. У вас есть ссылка на работника. Так что это не будет сбор мусора. –