我正在尝试编写一个执行可中断计算的网络工作者。我知道的唯一方法(Worker.terminate()
除外)是定期屈服于消息循环,以便它可以检查是否有任何新消息。例如,该网络工作者计算从0到data
的整数之和,但是如果在计算过程中向其发送新消息,它将取消计算并开始新的计算。
let currentTask = {
cancelled: false,
}
onmessage = event => {
// Cancel the current task if there is one.
currentTask.cancelled = true;
// Make a new task (this takes advantage of objects being references in Javascript).
currentTask = {
cancelled: false,
};
performComputation(currentTask, event.data);
}
// Wait for setTimeout(0) to complete, so that the event loop can receive any pending messages.
function yieldToMacrotasks() {
return new Promise((resolve) => setTimeout(resolve));
}
async function performComputation(task, data) {
let total = 0;
while (data !== 0) {
// Do a little bit of computation.
total += data;
--data;
// Yield to the event loop.
await yieldToMacrotasks();
// Check if this task has been superceded by another one.
if (task.cancelled) {
return;
}
}
// Return the result.
postMessage(total);
}
这有效,但速度慢得惊人。平均而言,while
循环的每次迭代在我的计算机上花费4毫秒!如果您希望取消很快发生,那将是一笔巨大的开销。
为什么这么慢?有没有更快的方法呢?
是,消息队列比超时队列具有更高的重要性,因此将以更高的频率触发。
您可以使用MessageChannel API轻松绑定到该队列:
let i = 0;
const channel = new MessageChannel();
channel.port1.onmessage = doSomething;
function doSomething() {
i++;
// loop
channel.port2.postMessage("");
}
doSomething();
// just to log
requestAnimationFrame( display );
function display() {
log.textContent = i;
requestAnimationFrame( display );
}
<pre id="log"></pre>
现在,您可能还希望每个事件循环批处理同一轮操作的几轮。