我正在研究 Javascript Web Workers。为此,我创建了以下页面和脚本:
worker-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>
worker-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>
元素。另外,当循环开始时,必须禁用“开始”按钮并启用“停止”按钮。当单击停止时,它会更改 running
将 Worker 中的标记设置为 false 并停止循环。此外,必须启用开始按钮并禁用停止按钮。总是当我单击按钮时,控制台会显示发送给工作人员的消息,然后显示 UI 线程从工作人员接收的消息。当我更改stepTime
时<SELECT>
值,工作线程暂停时间发生变化。 清理按钮只是清理 <TEXTAREA>
。
我真正得到的:当我点击开始时,<TEXTAREA>
充满了当前时间消息,并且控件根本不响应。实际上,所有浏览器实例(我在 Firefox 中测试)都会卡住并在一段时间后崩溃。显然,setTimeout()
虽然它在 UI 中工作,但在 Worker 中不起作用(例如,在控制台中我输入 setTimeout(function() {console.debug("PAUSE!!!")}, id("stepTime").value);
)。
我尝试的解决方法是替换 setTimeout()
调用 pause()
功能。我得到这个:页面(和浏览器)没有卡住。 <TEXTAREA>
显示时间消息,间隔最初在 <SELECT>
中设置。 。 清洁按钮按预期工作。但是,当循环启动时,它会在工作线程 ( console.debug("[WORKER] STARTING. running = " + running);
) 中显示控制台消息,但不显示工作线程响应 ( postMessage({msg: "STARTED"});
)。 开始 均未禁用,停止 均未启用。此外,如果我更改 <SELECT>
值,它不会改变间隔。如果我确实想更改间隔,我需要重新加载页面并设置另一个值。
查看代码,很明显为什么当我启动循环时没有调用响应,因为 run()
函数是无限循环,因此永远不会到达响应命令。
因此,我有两个问题:
- 如何更改代码以使其真正异步并且 react 灵敏。
- 为什么
setTimeout()
如果它应该根据 Web Worker definition 工作,则不工作.
谢谢
拉斐尔·阿方索
最佳答案
1)对于第一个问题: 这段代码:
function run() {
while(running) {
postMessage({msg: "TIME", time: new Date().toISOString()});
// pause();
setTimeout(function() {console.debug("PAUSE!!!")}, stepTime);
}
}
将会挂起你的浏览器,因为它会postMessage
与你的CPU运行一样快,因为你处于一种while(true)
状态,这会卡住你的网络 worker 。 pause
函数也会做同样的事情,基本上是一段时间(true)。
要使此代码异步运行,您应该控制何时在 WebWorker 内部调用 postMessage
,例如每完成作业 10% 一次……而不是在一段时间内为 true。
2)对于第二个问题: while 内的这行代码
setTimeout(function() {console.debug("PAUSE!!!")}, stepTime);
不会暂停它,它不会影响循环。在javascript中没有sleep类似 php 的方法。
要达到此效果,您需要将 run 函数替换为以下函数:
function run() {
if(running){
postMessage({msg: "TIME", time: new Date().toISOString()});
setTimeout(function() { run(); }, stepTime);
}
}
关于javascript - 如何让 Webworker 真正响应以及为什么 setTimeout() 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29234495/