Is there a faster alternative to window.requestAnimationFrame()
for endless loops that don't block I/O?
对于不阻止I / O的无限循环,window.requestAnimationFrame()是否有更快的替代方法?
What I'm doing in the loop isn't related to animation so I don't care when the next frame is ready, and I have read that window.requestAnimationFrame()
is capped by the monitor's refresh rate or at least waits until a frame can be drawn.
我在循环中做的事与动画无关,所以我不关心下一帧是否准备就绪,并且我已经读过window.requestAnimationFrame()受到监视器刷新率的限制或至少等到框架可以绘制。
I have tried the following as well:
我也尝试了以下内容:
function myLoop() {
// stuff in loop
setTimeout(myLoop, 4);
}
(The 4 is because that is the minimum interval in setTimeout
and smaller values will still default to 4.) However, I need better resolution than this.
(4是因为这是setTimeout中的最小间隔,较小的值仍将默认为4.)但是,我需要比此更好的分辨率。
Is there anything with even better performance out there?
那里有更好的表现吗?
I basically need a non-blocking version of while(true)
.
我基本上需要while(true)的非阻塞版本。
2 个解决方案
#1
5
Two things that will run sooner than that setTimeout
:
比setTimeout更早运行的两件事:
-
process.nextTick
callbacks (NodeJS-specific):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.process.nextTick()方法将回调添加到“下一个滴答队列”。一旦事件循环的当前转弯转到完成,将调用当前在下一个滴答队列中的所有回调。
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.这不是setTimeout(fn,0)的简单别名。效率更高。它在事件循环的后续滴答中触发任何其他I / O事件(包括定时器)之前运行。
-
Promise settlement notifications
承诺结算通知
So those might be a tools for your toolbelt, doing a mix of one or both of those with setTimeout
to achieve the balance you want.
所以那些可能是你的工具带的工具,混合使用setTimeout中的一个或两个来实现你想要的平衡。
Details:
As you probably know, a given JavaScript thread runs on the basis of a task queue (the spec calls it a job queue); and as you probably know, there's one main default UI thread in browsers and NodeJS runs a single thread.
您可能知道,给定的JavaScript线程基于任务队列运行(规范将其称为作业队列);你可能知道,浏览器中有一个主要的默认UI线程,而NodeJS运行一个线程。
But in fact, there are at least two task queues in modern implementations: The main one we all think of (where setTimeout
and event handlers put their tasks), and the "microtask" queue where certain async operations are placed during the processing of a main task (or "macrotask"). Those microtasks are processed as soon as the macrotask completes, before the next macrotask in the main queue — even if that next macrotask was queued before the microtasks were.
但事实上,在现代实现中至少有两个任务队列:我们都想到的主要任务队列(其中setTimeout和事件处理程序放置他们的任务),以及“微任务”队列,其中在处理过程中放置了某些异步操作主要任务(或“macrotask”)。一旦macrotask完成,就会在主队列中的下一个macrotask之前处理这些微任务 - 即使下一个macrotask在微任务之前排队。
nextTick
callbacks and promise settlement notifications are both microtasks. So scheduling either schedules an async callback, but one which will happen before the next main task.
nextTick回调和承诺结算通知都是微任务。因此,调度要么调度异步回调,要么调度在下一个主要任务之前发生的回调。
We can see that in the browser with setInterval
and a promise resolution chain:
我们可以在带有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>
When you run that and click the Hog button, note how the counter display freezes, then keeps going again. That's because of the 300,000 microtasks that get scheduled ahead of it. Also note the timestamps on the three log messages we write (they don't appear in the snippet console until a macrotask displays them, but the timestamps show us when they were logged).
当你运行它并单击Hog按钮时,请注意计数器显示如何冻结,然后再继续。这是因为在它之前安排了300,000个微型计划。另请注意我们编写的三条日志消息的时间戳(它们不会出现在代码段控制台中,直到macrotask显示它们,但时间戳显示我们何时记录它们)。
So basically, you could schedule a bunch of microtasks, and periodically let those run out and run the next macrotask.
所以基本上,你可以安排一堆微任务,并定期让那些用完并运行下一个macrotask。
Note: I've used setInterval
for the browser example in the snippet, but setInterval
, specifically, may not be a good choice for a similar experiment using NodeJS, as NodeJS's setInterval
is a bit different from the one in browsers and has some surprising timing characteristics.
注意:我在代码片段中使用了setInterval作为浏览器示例,但具体来说,setInterval对于使用NodeJS的类似实验可能不是一个好选择,因为NodeJS的setInterval与浏览器中的setInterval略有不同,并且有一些令人惊讶的时机特点。
#2
0
there are some libs that can work like cron task, e.g., https://www.npmjs.com/package/node-cron
有些库可以像cron任务一样工作,例如https://www.npmjs.com/package/node-cron
i think that using cron should be easier, and more flexible.
我认为使用cron应该更容易,更灵活。
#1
5
Two things that will run sooner than that setTimeout
:
比setTimeout更早运行的两件事:
-
process.nextTick
callbacks (NodeJS-specific):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.process.nextTick()方法将回调添加到“下一个滴答队列”。一旦事件循环的当前转弯转到完成,将调用当前在下一个滴答队列中的所有回调。
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.这不是setTimeout(fn,0)的简单别名。效率更高。它在事件循环的后续滴答中触发任何其他I / O事件(包括定时器)之前运行。
-
Promise settlement notifications
承诺结算通知
So those might be a tools for your toolbelt, doing a mix of one or both of those with setTimeout
to achieve the balance you want.
所以那些可能是你的工具带的工具,混合使用setTimeout中的一个或两个来实现你想要的平衡。
Details:
As you probably know, a given JavaScript thread runs on the basis of a task queue (the spec calls it a job queue); and as you probably know, there's one main default UI thread in browsers and NodeJS runs a single thread.
您可能知道,给定的JavaScript线程基于任务队列运行(规范将其称为作业队列);你可能知道,浏览器中有一个主要的默认UI线程,而NodeJS运行一个线程。
But in fact, there are at least two task queues in modern implementations: The main one we all think of (where setTimeout
and event handlers put their tasks), and the "microtask" queue where certain async operations are placed during the processing of a main task (or "macrotask"). Those microtasks are processed as soon as the macrotask completes, before the next macrotask in the main queue — even if that next macrotask was queued before the microtasks were.
但事实上,在现代实现中至少有两个任务队列:我们都想到的主要任务队列(其中setTimeout和事件处理程序放置他们的任务),以及“微任务”队列,其中在处理过程中放置了某些异步操作主要任务(或“macrotask”)。一旦macrotask完成,就会在主队列中的下一个macrotask之前处理这些微任务 - 即使下一个macrotask在微任务之前排队。
nextTick
callbacks and promise settlement notifications are both microtasks. So scheduling either schedules an async callback, but one which will happen before the next main task.
nextTick回调和承诺结算通知都是微任务。因此,调度要么调度异步回调,要么调度在下一个主要任务之前发生的回调。
We can see that in the browser with setInterval
and a promise resolution chain:
我们可以在带有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>
When you run that and click the Hog button, note how the counter display freezes, then keeps going again. That's because of the 300,000 microtasks that get scheduled ahead of it. Also note the timestamps on the three log messages we write (they don't appear in the snippet console until a macrotask displays them, but the timestamps show us when they were logged).
当你运行它并单击Hog按钮时,请注意计数器显示如何冻结,然后再继续。这是因为在它之前安排了300,000个微型计划。另请注意我们编写的三条日志消息的时间戳(它们不会出现在代码段控制台中,直到macrotask显示它们,但时间戳显示我们何时记录它们)。
So basically, you could schedule a bunch of microtasks, and periodically let those run out and run the next macrotask.
所以基本上,你可以安排一堆微任务,并定期让那些用完并运行下一个macrotask。
Note: I've used setInterval
for the browser example in the snippet, but setInterval
, specifically, may not be a good choice for a similar experiment using NodeJS, as NodeJS's setInterval
is a bit different from the one in browsers and has some surprising timing characteristics.
注意:我在代码片段中使用了setInterval作为浏览器示例,但具体来说,setInterval对于使用NodeJS的类似实验可能不是一个好选择,因为NodeJS的setInterval与浏览器中的setInterval略有不同,并且有一些令人惊讶的时机特点。
#2
0
there are some libs that can work like cron task, e.g., https://www.npmjs.com/package/node-cron
有些库可以像cron任务一样工作,例如https://www.npmjs.com/package/node-cron
i think that using cron should be easier, and more flexible.
我认为使用cron应该更容易,更灵活。