I am trying to write a web worker that performs an interruptible computation. The only way to do that (other than Worker.terminate()) that I know is to periodically yield to the message loop so it can check if there are any new messages. For example this web worker calculates the sum of the integers from 0 to data, but if you send it a new message while the calculation is in progress it will cancel the calculation and start a new one.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毫秒!如果您希望取消很快发生,那将是一笔巨大的开销.This works but it is appallingly slow. On average each iteration of the while loop takes 4 ms on my machine! That is a pretty huge overhead if you want cancellation to happen quickly.为什么这么慢?还有更快的方法吗?Why is this so slow? And is there a faster way to do this?推荐答案是的,消息队列的重要性高于 timeout 队列,因此将以更高的优先级触发频率.Yes, the message queue will have higher importance than timeouts one, and will thus fire at higher frequency.您可以使用 MessageChannel API :let i = 0;let j = 0;const channel = new MessageChannel();channel.port1.onmessage = messageLoop;function messageLoop() { i++; // loop channel.port2.postMessage("");}function timeoutLoop() { j++; setTimeout( timeoutLoop );}messageLoop();timeoutLoop();// just to logrequestAnimationFrame( display );function display() { log.textContent = "message: " + i + '\n' + "timeout: " + j; requestAnimationFrame( display );}<pre id="log"></pre>现在,您可能还希望在每个事件循环中分批进行同一轮操作.Now, you may also want to batch several rounds of the same operation per event loop. 根据规范, setTimeout 将在调用的第5级之后(即OP循环的第5次迭代之后)被限制为至少4毫秒.邮件事件不受此限制.Per specs, setTimeout will get throttled to a minimum of 4ms after the 5th level of call, that is after the fifth iteration of OP's loop.Message events are not subject to this limitation.某些情况下,某些浏览器会将由 setTimeout 发起的任务的优先级降低.即, Firefox在页面加载时会这样做,以便脚本调用此时,setTimeout 不会阻止其他事件;他们甚至为此创建一个任务队列.即使仍未指定,似乎至少在Chrome中,消息事件也具有用户可见"的优先级,这意味着某些UI事件可能首先出现,但仅此而已.(使用即将在Chrome中使用的 scheduler.postTask() API对此进行了测试)Some browsers will make the task initiated by setTimeout have a lower priority, in some cases.Namely, Firefox does that at page loading, so that scripts calling setTimeout at this moment don't block other events ; they do even create a task queue just for that.Even if still un-specced, it seems that at least in Chrome, message events have a "user-visible" priority, which means some UI events could come first, but that's about it. (Tested this using the up-coming scheduler.postTask() API in Chrome)大多数现代浏览器都会在页面不可见时限制默认超时,并且此甚至可以申请工人.邮件事件不受此限制.Most modern browsers will throttle default timeouts when the page is not visible, and this may even apply for Workers.Message events are not subject to this limitation. 由OP 发现, Chrome的确为前5个设置了至少1ms的时间通话. 但是请记住,如果所有这些限制都已置于 setTimeout 上,那是因为以这样的速率安排许多任务会产生成本.But remember that if all these limitations have been put on setTimeout, it's because scheduling that many tasks at such a rate has a cost.在Window上下文中执行此操作将限制浏览器必须处理的所有常规任务,但它们会被认为不太重要,例如网络请求,垃圾收集等.另外,发布新任务意味着事件循环必须在高频下运行,并且永远都不会闲置,这意味着更多的能源消耗.Doing this in a Window context will throttle all the normal tasks the browser has to handle, but which they'll consider less important, like Network requests, Garbage Collection etc.Also, posting a new task means that the event loop has to run at high frequency and will never idle, which means more energy consumption. 这篇关于有没有比setTimeout(0)更快的方法来产生Javascript事件循环?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
09-05 11:15
查看更多