有两个不错的选择。
备选案文1:
Worker.terminate()
第一种方法是杀死你现有的网络工作者,然后开始一个新的工作。你可以用
Worker.terminate()
.
这个
terminate()
方法
Worker
工人
-
你失去了所有的工人。如果您必须为请求将大量数据复制到其中,那么您必须再次执行此操作。
-
它涉及到线程的创建和销毁,这并不像大多数人想象的那么慢,但是如果你终止web workers,那么
这可能会引起一些问题。
如果这两个都不是问题,那么这可能是最简单的选择。
就我而言,我有很多州。我的工作人员正在渲染图像的一部分,当用户平移到其他区域时,我希望它停止正在执行的操作并开始渲染新区域。但是渲染图像所需的数据非常庞大。
在你的情况下,你有你不想使用的(大概是巨大的)列表的状态。
选择2:屈服
第二种选择基本上是协同多任务处理。您可以正常运行计算,但偶尔会暂停(屈服)并说“我应该停止吗?”,就像这样(这是为了一些无意义的计算,而不是排序)。
let requestId = 0;
onmessage = event => {
++requestId;
sortAndSendData(requestId, event.data);
}
function sortAndSendData(thisRequestId, data) {
let isSorted = false;
let total = 0;
while (data !== 0) {
// Do a little bit of computation.
total += data;
--data;
// Check if we are still the current request ID.
if (thisRequestId !== requestId) {
// Data was changed. Cancel this sort.
return;
}
}
postMessage(total);
}
但这行不通,因为
sortAndSendData()
运行到完成并阻止web worker的事件循环。我们需要一些方法在这之前让步
thisRequestId !== requestId
. 不幸的是,Javascript没有很好的应用程序
yield
方法它确实有
async
await
所以我们可以试试这个:
let requestId = 0;
onmessage = event => {
console.log("Got event", event);
++requestId;
sortAndSendData(requestId, event.data);
}
async function sortAndSendData(thisRequestId, data) {
let isSorted = false;
let total = 0;
while (data !== 0) {
// Do a little bit of computation.
total += data;
--data;
await Promise.resolve();
// Check if we are still the current request ID.
if (thisRequestId !== requestId) {
console.log("Cancelled!");
// Data was changed. Cancel this sort.
return;
}
}
postMessage(total);
}
不幸的是,它不起作用。我想是因为
异步的
/
等候
使用“微任务”急切地执行任务,如果可能,在挂起“宏任务”(我们的web worker消息)之前执行这些任务。
等候
要成为一个宏任务,可以使用
setTimeout(0)
let requestId = 0;
onmessage = event => {
console.log("Got event", event);
++requestId;
sortAndSendData(requestId, event.data);
}
function yieldToMacrotasks() {
return new Promise((resolve) => setTimeout(resolve));
}
async function sortAndSendData(thisRequestId, data) {
let isSorted = false;
let total = 0;
while (data !== 0) {
// Do a little bit of computation.
total += data;
--data;
await yieldToMacrotasks();
// Check if we are still the current request ID.
if (thisRequestId !== requestId) {
console.log("Cancelled!");
// Data was changed. Cancel this sort.
return;
}
}
postMessage(total);
}
这管用!但是它非常慢。
await yieldToMacrotasks()
设置超时(0)
幸运的是,另一位用户向我指出
a quicker way
. 基本上是在另一台计算机上发送消息
MessageChannel
也会屈服于事件循环,但不受最小延迟的影响,如
是此代码有效,每个循环只需约0.04 ms,这应该可以。
let currentTask = {
cancelled: false,
}
onmessage = event => {
currentTask.cancelled = true;
currentTask = {
cancelled: false,
};
performComputation(currentTask, event.data);
}
async function performComputation(task, data) {
let total = 0;
let promiseResolver;
const channel = new MessageChannel();
channel.port2.onmessage = event => {
promiseResolver();
};
while (data !== 0) {
// Do a little bit of computation.
total += data;
--data;
// Yield to the event loop.
const promise = new Promise(resolve => {
promiseResolver = resolve;
});
channel.port1.postMessage(null);
await promise;
// Check if this task has been superceded by another one.
if (task.cancelled) {
return;
}
}
// Return the result.
postMessage(total);
}
我对此并不完全满意——这取决于
postMessage()
事件以FIFO顺序处理,我怀疑这是有保证的。我怀疑你可以重写代码使它工作,即使那不是真的。