我在chrome上发起了一个web worker,它有一个简单的函数,使用setTimeout
重复调用。令人惊讶的是,在调用函数大约1000次之后,Web worker终止了。有谁能解释为什么?我猜chrome正在做一些优化。
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
};
编辑: 转载在小提琴中: http://jsfiddle.net/meovfpv3/1/ 对于onmessage回调来说,停止射击似乎需要很长时间,只需几秒钟,然后长达+5分钟
答案 0 :(得分:5)
这是我对正在发生的事情的最好猜测。通过每1ms从Web Worker发布一条消息,您要求主线程在1ms内处理每个发布的消息。
如果主线程无法在1ms内处理该消息,即使它尚未处理完最后一条消息,您仍然会向其发送新消息。我想这会将它放入等待处理的消息队列中。
现在,由于您从Web工作者发送的消息比处理它们的速度快,因此未处理消息的队列将变得越来越大。在某些时候,Chrome会举起手来说“#34;队列中有太多的消息"”,而不是排队新消息进行处理,它会丢弃它们。
这就是为什么如果你在超时中使用一个合理的数字,比如100ms,那么在发送下一条消息之前,消息有足够的时间进行处理,并且没有发生未处理消息的问题。
我创建了一个jsFiddle,其中worker向主线程发送消息,主线程将消息发送回worker。如果在发送下一条消息之前该过程没有发生,则两个线程中的计数器将不匹配,并且Web worker将终止。
http://jsfiddle.net/meovfpv3/3/
您可以看到,使用100ms的合理setTimeout,所有消息都有足够的时间在下一条消息发生之前进行处理。
当你将setTimeout降低到1ms时,消息链在发送下一条消息之前没有时间完成,并且每个线程中的计数器最终都会被删除,绊倒if
子句并终止网络工作者。
解决此问题的一种方法是,不是每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);
}
答案 1 :(得分:4)
首先,一些观察,我无法解释,但有点有趣,可能会激发某些人的灵感:
@Anson - 如果我将你的jsFiddle代码放入Codepen(仍在Chrome中),那里就没有问题了。 onmessage
回调只是继续工作!
回到jsFiddle ......它甚至无法将setTimeout
更改为像10s那样的长差距,因此它不是工作人员发布消息的次数,而是onmessage
之前的时间。{ 1}}回调停止解雇 - 这有很多差异。
然后我找到了一些方法来保持onmessage
处理程序在这个特定的例子中保持活着:
$("#stop").on("click",function(e){e.preventDefault();worker.terminate();});
console.log(worker)
后,只需添加onmessage
。window.worker = worker
后添加onmessage
。在所有情况下再次提及worker
似乎让它保持活力。
答案 2 :(得分:1)
你是否每隔1ms尝试一次postMessage?那么你可能想要使用https://regex101.com/r/oE7xG8/2:
setInterval(function(){
postMessage('1');
}, 1);
编辑:我错误地看到了不存在的递归,只是因为我在寻找它。我仍会使用setInterval
而不是setTimeout
。