我正在研究Javascript Web Workers。为此,我在下面创建了页面和脚本:
工人的test.html
<!DOCTYPE html>
<html>
<head>
<title>Web Worker Test</title>
<script src="worker-test.js"></script>
</head>
<body onload="init()">
<form>
<table width="50%">
<tr style="text-align: center;">
<td width="25%">
<label >Step Time</label>
<select id="stepTime" onchange="changeStepTime();">
<option>500</option>
<option>1000</option>
<option>2000</option>
</select>
</td>
<td width="25%">
<button id="btnStart" onclick="return buttonAction('START');">Start</start>
</td>
<td width="25%">
<button id="btnStop" onclick="return buttonAction('STOP');">Stop</start>
</td>
<td width="25%">
<button id="btnClean" onclick="return clean();">Clean</start>
</td>
</tr>
<tr>
<td colspan="4">
<textarea id="log" rows="15" style="width: 100%; font-size: xx-large;" readonly="readonly"></textarea>
</td>
</tr>
</table>
</form>
</body>
</html>
工人test.js
function id(strId) {
return document.getElementById(strId);
}
worker = new Worker('worker.js');
worker.onmessage = function(e) {
switch(e.data.msg) {
case "STARTED":
console.info(e.data);
id("btnStart").disabled = true;
id("btnStop").disabled = false;
break;
case "STOPPED":
console.info(e.data);
id("btnStart").disabled = false;
id("btnStop").disabled = true;
break;
case "TIME":
id("log").value += e.data.time + '\n';
break
default:
// ...
}
};
function buttonAction(action) {
worker.postMessage({msg: action});
return false;
}
function changeStepTime() {
worker.postMessage({msg: "STEPTIME", stepTime: id("stepTime").value});
}
function clean() {
id('log').value = '';
return false;
}
function init() {
id("btnStop").disabled = true;
changeStepTime();
}
worker.js
var running = false;
var stepTime = 0;
// Replacement of setTimeout().
function pause() {
var t0 = Date.now();
while((Date.now() - t0) < stepTime) {
(function() {}) ();
}
}
function run() {
while(running) {
postMessage({msg: "TIME", time: new Date().toISOString()});
// pause();
setTimeout(function() {console.debug("PAUSE!!!")}, stepTime);
}
}
addEventListener('message', function(e) {
switch(e.data.msg) {
case "START":
running = true;
console.debug("[WORKER] STARTING. running = " + running);
run();
postMessage({msg: "STARTED"});
break;
case "STOP":
running = false;
console.debug("[WORKER] STOPPING. running = " + running);
postMessage({msg: "STOPPED"});
break;
case "STEPTIME":
stepTime = parseInt(e.data.stepTime);
console.debug("[WORKER] STEP TIME = " + stepTime);
postMessage({msg: "STEP TIME CHANGED"});
break;
default:
console.warn("[WORKER] " + e.data);
}
});
页面应该如何操作:当我点击开始按钮时,它会向工作人员发送一条消息,该消息是调用运行函数。在这个函数中有一个while循环。在那里,首先向UI线程发送一条消息,其中当前时间显示在<TEXTAREA>
中。接下来,调用setTimeout()
在stepTime
<SELECT>
元素设置的时间内暂停。此外,当循环开始时,必须禁用 Start 按钮并启用 Stop 。单击 Stop 时,它会将Worker中的running
标志更改为false并停止循环。此外,必须启用开始按钮并禁用停止。总是当我单击按钮时,控制台会显示发送给worker的消息,然后显示来自Worker的UI线程接收的消息。当我更改stepTime
<SELECT>
值时,工作人员暂停时间会发生变化。 Clean 按钮只是清除<TEXTAREA>
。
我真正得到的:当我点击开始时,<TEXTAREA>
充满了当前时间消息,控件根本没有响应。实际上所有的浏览器实例(我在Firefox中测试过)都会冻结并在一段时间后崩溃。显然,setTimeout()
在Worker中不起作用,但在UI中起作用(例如,在控制台中我键入setTimeout(function() {console.debug("PAUSE!!!")}, id("stepTime").value);
)。
我尝试过的解决方法是用setTimeout()
函数替换pause()
调用。我明白了:页面(和浏览器)没有冻结。 <TEXTAREA>
显示时间消息,最初在<SELECT>
中设置的时间间隔。 Clean 按钮按预期工作。但是,当循环启动时,它会在worker(console.debug("[WORKER] STARTING. running = " + running);
)中显示控制台消息,但不显示工作者响应(postMessage({msg: "STARTED"});
)。 Start 未启用, stop 未启用。此外,如果我更改<SELECT>
值,则不会更改间隔。如果我真的想要更改间隔,我需要重新加载页面并设置另一个值。
查看代码很明显为什么在启动循环时没有调用响应,因为run()
函数是一个无限循环,因此永远不会达到响应命令。
因此,我有两个问题:
setTimeout()
符合Web Worker definition 谢谢,
Rafael Afonso
答案 0 :(得分:3)
1)对于第一个问题: 这段代码:
function run() {
while(running) {
postMessage({msg: "TIME", time: new Date().toISOString()});
// pause();
setTimeout(function() {console.debug("PAUSE!!!")}, stepTime);
}
}
会挂起您的浏览器,因为它会{c}运行速度postMessage
,因为您在某个while(true)
里面会冻结您的网络工作者。 pause
函数也会这样做,基本上它是一段时间(true)。
要使此代码以异步方式运行,您应该控制何时在您的网络工程师中调用postMessage
,例如每10%的工作完成...不会在一段时间内完成。
2)对于第二个问题: 这行代码在while
中setTimeout(function() {console.debug("PAUSE!!!")}, stepTime);
不会暂停它,它不会影响循环。在javascript中没有像php一样的sleep方法。
要获得所需的效果,请将run函数替换为:
function run() {
if(running){
postMessage({msg: "TIME", time: new Date().toISOString()});
setTimeout(function() { run(); }, stepTime);
}
}
答案 1 :(得分:1)
albanx:
根据您的建议,我更改了worker.js
代码,如下所示:
var running = false;
var stepTime = 0;
function run() {
if(running) {
postMessage({msg: "TIME", time: new Date().toISOString()});
setTimeout(run, stepTime);
}
}
self.addEventListener('message', function(e) {
switch(e.data.msg) {
case "START":
running = true;
console.debug("[WORKER] STARTING. running = " + running);
postMessage({msg: "STARTED"});
run();
break;
case "STOP":
running = false;
console.debug("[WORKER] STOPPING. running = " + running);
postMessage({msg: "STOPPED"});
break;
case "STEPTIME":
stepTime = parseInt(e.data.stepTime);
console.debug("[WORKER] STEP TIME = " + stepTime);
postMessage({msg: "STEP TIME CHANGED"});
break;
default:
console.warn("[WORKER] " + e.data);
}
});
这一次都按预期工作:停止按钮已启用并且确实停止了循环。也可以更改改变<SELECT>
的步骤时间。最后,我可能需要多次重启循环。
我唯一的问题是:这些对run
(setTimeout(run, stepTime);
)的recrsive调用是否可以创建堆栈溢出?
谢谢,
Rafael Afonso