Attempting to create a web worker that can pause during computation is proving to be challenging. As of now, the only method I am aware of (aside from using Worker.terminate()
) involves periodically allowing the message loop to check for any incoming messages. For instance, the current web worker calculates the sum of integers from 0 to data
, but if a new message is received while the calculation is ongoing, it will halt the current process and initiate a new one.
let currentTask = {
cancelled: false,
}
onmessage = event => {
// Cancel the current task if there is one.
currentTask.cancelled = true;
// Start a new task (leveraging JavaScript object reference).
currentTask = {
cancelled: false,
};
performComputation(currentTask, event.data);
}
// Wait for setTimeout(0) to finish in order to receive pending messages.
function yieldToMacrotasks() {
return new Promise((resolve) => setTimeout(resolve));
}
async function performComputation(task, data) {
let total = 0;
while (data !== 0) {
// Perform some calculations.
total += data;
--data;
// Yield to the event loop.
await yieldToMacrotasks();
// Check if this task has been superseded by another one.
if (task.cancelled) {
return;
}
}
// Return the result.
postMessage(total);
}
While this approach works, it is incredibly slow. Each iteration of the while
loop on average takes 4 ms on my machine! This overhead significantly impacts cancellation speed.
What is causing this slow performance? Are there quicker alternatives to achieve interruptible computations?