有两个不错的选择。
选项1: Worker.terminate()
第一个是杀死你现有的网络工作者并开始一个新的。为此,您可以使用Worker.terminate().
接口的terminate()方法Worker立即终止Worker. 这不会为工人提供完成其操作的机会;它只是立即停止。
这种方法的唯一缺点是:
- 你失去了所有的工人状态。如果您必须为请求将大量数据复制到其中,则必须再次执行所有操作。
 
- 它涉及线程的创建和销毁,这并不像大多数人想象的那么慢,但是如果您大量终止 Web Worker,则可能会导致问题。
 
如果这些都不是问题,那么它可能是最简单的选择。
就我而言,我有很多状态。我的工作人员正在渲染图像的一部分,当用户平移到不同的区域时,我希望它停止正在执行的操作并开始渲染新区域。但是渲染图像所需的数据非常庞大。
在您的情况下,您拥有不想使用的(可能很大)列表的状态。
选项 2:屈服
第二种选择基本上是进行协作多任务处理。你像往常一样运行你的计算,但你时不时地暂停(yield)并说“我应该停止吗?”,就像这样(这是一些无意义的计算,而不是排序)。
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);
}
不幸的是它不起作用。我认为这是因为async/await使用“微任务”急切地执行事情,如果可能的话,这些“微任务”会在待处理的“宏任务”(我们的网络工作者消息)之前执行。
我们需要强制我们await成为一个宏任务,你可以使用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()在我的带有 Chrome 的机器上大约需要 4 毫秒!这是因为浏览器将最小超时设置为setTimeout(0)1 或 4 毫秒(实际的最小值似乎很复杂)。
幸运的是,另一位用户向我指出了一种更快的方法。基本上在另一个上发送消息MessageChannel也会产生事件循环,但不受最小延迟setTimeout(0)的影响。这段代码有效,每个循环只需要大约 0.04 毫秒,应该没问题。
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 顺序处理的事件,我怀疑这是有保证的。我怀疑您可以重写代码以使其工作,即使那不是真的。