对于不阻塞 I/O 的无限循环,是否有比 window.requestAnimationFrame()
更快的替代方法?
我在循环中所做的与动画无关,所以我不关心下一帧何时准备就绪,而且我已经读到 window.requestAnimationFrame()
的上限为显示器的刷新率或至少等到可以绘制帧。
我也尝试过以下方法:
function myLoop() {
// stuff in loop
setTimeout(myLoop, 4);
}
(4 是因为这是 setTimeout
中的最小间隔,较小的值仍将默认为 4。)但是,我需要比这更好的分辨率。
有什么性能更好的东西吗?
我基本上需要 while(true)
的非阻塞版本。
最佳答案
有两件事会比 setTimeout
运行得更快:
process.nextTick
回调(特定于 NodeJS):The
process.nextTick()
method adds the callback to the "next tick queue". Once the current turn of the event loop turn runs to completion, all callbacks currently in the next tick queue will be called.This is not a simple alias to
setTimeout(fn, 0)
. It is much more efficient. It runs before any additional I/O events (including timers) fire in subsequent ticks of the event loop.promise 结算通知
所以这些可能是您的工具带的工具,将其中一个或两个与 setTimeout
混合使用以实现您想要的平衡。
详细信息:
您可能知道,给定的 JavaScript 线程在任务队列(规范称之为作业队列)的基础上运行;您可能知道,浏览器中有一个主要的默认 UI 线程,而 NodeJS 运行单个线程。
但实际上,现代实现中至少有两种任务队列:我们都想到的主要的(setTimeout
和事件处理程序放置任务的地方),以及“微任务”队列,其中某些异步操作被放置在主任务(或“宏任务”)的处理过程中。这些微任务会在宏任务完成后立即处理,在主队列中的下一个宏任务之前——即使下一个宏任务在微任务之前排队。
nextTick
回调和 promise 结算通知都是微任务。因此,调度要么调度一个异步回调,要么调度一个将在下一个主要任务之前发生的回调。
我们可以在带有 setInterval
和 promise 解析链的浏览器中看到:
let counter = 0;
// setInterval schedules macrotasks
let timer = setInterval(() => {
$("#ticker").text(++counter);
}, 100);
// Interrupt it
$("#hog").on("click", function() {
let x = 300000;
// Queue a single microtask at the start
Promise.resolve().then(() => console.log(Date.now(), "Begin"));
// `next` schedules a 300k microtasks (promise settlement
// notifications), which jump ahead of the next task in the main
// task queue; then we add one at the end to say we're done
next().then(() => console.log(Date.now(), "End"));
function next() {
if (--x > 0) {
if (x === 150000) {
// In the middle; queue one in the middle
Promise.resolve().then(function() {
console.log(Date.now(), "Middle");
});
}
return Promise.resolve().then(next);
} else {
return 0;
}
}
});
$("#stop").on("click", function() {
clearInterval(timer);
});
<div id="ticker"> </div>
<div><input id="stop" type="button" value="Stop"></div>
<div><input id="hog" type="button" value="Hog"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
当您运行它并单击 Hog 按钮时,请注意计数器显示如何卡住,然后再次继续显示。这是因为提前安排了 300,000 个微任务。还要注意我们编写的三个日志消息上的时间戳(它们不会出现在代码片段控制台中,直到宏任务显示它们,但时间戳会在它们被记录时显示给我们)。
所以基本上,您可以安排一堆微任务,并定期让这些微任务用完并运行下一个宏任务。
注意:我在代码段中的浏览器示例中使用了 setInterval
,但具体而言,setInterval
可能不是一个好的选择对于使用 NodeJS 的类似实验,因为 NodeJS 的 setInterval
与浏览器中的有点不同,并且具有一些令人惊讶的计时特性。
关于javascript - 非常快速的无限循环,不会阻塞 I/O,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42547070/