node.js:setInterval()正在跳过调用
node.js: setInterval() skipping calls
对于即将推出的node.js项目,我需要定期执行各种内务管理任务。特别是一些任务每毫秒执行一次,另一些任务每20ms执行一次(每秒50次),还有一些任务每秒钟执行一次。所以我考虑使用setInterval(),结果很有趣:许多函数调用都被跳过了。
我使用的基准如下:
var counter = 0;
var seconds = 0;
var short = 1;
setInterval(function() {
counter ++;
}, short);
setInterval(function() {
seconds ++;
log('Seconds: ' + seconds + ', counter: ' +
counter + ', missed ' +
(seconds * 1000 / short - counter));
}, 1000);
有一个1秒的长计时器和一个短计时器,可以使用变量short
进行调整,在本例中为1毫秒。我们每秒打印短周期中预期的节拍数与短计数器更新的实际次数之间的差值。
以下是短计时器为1ms时的行为:
2012-09-14T23:03:32.780Z Seconds: 1, counter: 869, missed 131
2012-09-14T23:03:33.780Z Seconds: 2, counter: 1803, missed 197
2012-09-14T23:03:34.781Z Seconds: 3, counter: 2736, missed 264
...
2012-09-14T23:03:41.783Z Seconds: 10, counter: 9267, missed 733
许多函数调用被跳过。这是10毫秒:
2012-09-14T23:01:56.363Z Seconds: 1, counter: 93, missed 7
2012-09-14T23:01:57.363Z Seconds: 2, counter: 192, missed 8
2012-09-14T23:01:58.364Z Seconds: 3, counter: 291, missed 9
...
2012-09-14T23:02:05.364Z Seconds: 10, counter: 986, missed 14
更好,但大约每秒跳过一个函数调用。持续20毫秒:
2012-09-14T23:07:18.713Z Seconds: 1, counter: 46, missed 4
2012-09-14T23:07:19.713Z Seconds: 2, counter: 96, missed 4
2012-09-14T23:07:20.712Z Seconds: 3, counter: 146, missed 4
...
2012-09-14T23:07:27.714Z Seconds: 10, counter: 495, missed 5
最后100毫秒:
2012-09-14T23:04:25.804Z Seconds: 1, counter: 9, missed 1
2012-09-14T23:04:26.803Z Seconds: 2, counter: 19, missed 1
2012-09-14T23:04:27.804Z Seconds: 3, counter: 29, missed 1
...
2012-09-14T23:04:34.805Z Seconds: 10, counter: 99, missed 1
在这种情况下,它跳过了很少的呼叫(33秒后差距增加到2,108秒后差距扩大到3
这些数字各不相同,但在不同的运行之间惊人地一致:运行第一个1毫秒的基准测试三次,在10秒后产生了9267、9259和9253的延迟。
我没有找到关于这个特定问题的参考资料。有很多引用Ressig的帖子和许多相关的JavaScript问题,但大多数人认为代码运行在浏览器中,而不是node.js中
现在来问一个可怕的问题:这里发生了什么?只是开玩笑;显然,函数调用被跳过了。但我看不出这种模式。我认为长周期可能会阻止短周期,但在1毫秒的情况下没有任何意义。短周期函数调用不会重叠,因为它们只是更新一个变量,即使在1毫秒的短周期内,node.js进程的CPU也接近5%。不过,平均负载很高,约为0.50。不过,我不知道为什么一千个调用会给我的系统带来如此大的压力,因为node.js可以完美地处理更多的客户端;setInterval()确实占用了CPU(或者我做错了什么)。
一个明显的解决方案是使用较长的定时器对函数调用进行分组,然后多次运行短周期函数调用以模拟较短的定时器。然后使用长周期作为"扫帚车",在较低的间隔内拨打任何未接来电。例如:设置20ms和1000ms的setInterval()调用。对于1毫秒的调用:在20毫秒的回调中调用它们20次。对于1000ms调用:检查20ms函数被调用了多少次(例如47次),执行任何剩余调用(例如3次)。但这个方案会有点复杂,因为通话可能会以有趣的方式重叠;它也不会是常规的,尽管它可能看起来像。
真正的问题是:使用setInterval()或node.js中的其他计时器,它能做得更好吗?提前谢谢。javascript中的
SetInterval函数不准确。您应该尝试使用高分辨率计时器。在javascript 中构建准确的计时器
查看此文档:http://nodejs.org/api/timers.html#timers_settimeout_callback_delay_arg
需要注意的是,回调可能不会在延迟毫秒内被调用-Node.js无法保证回调何时启动的确切时间,也无法保证启动的顺序。回调将在尽可能接近指定时间的情况下被调用。
发生这种情况是因为应用程序代码阻塞了事件循环。所有定时器和I/O事件只能在nextTick
上处理。
您可以通过以下代码看到这种行为:
setInterval(function() {
console.log(Date.now());
for (var i = 0; i < 100000000; i++) {
}
}, 1);
尝试更改迭代次数并查看结果。
理想情况下,如果应用程序的滴答声持续时间不到一毫秒,计时器就会被触发。但这在实际应用程序中是不可行的。
答案恰好是Vadim和zer02给出的答案的组合,所以我在这里留下一篇文章。正如瓦迪姆所说,该系统无法应对过于频繁的更新,给系统增加一些负载也无济于事。或者更确切地说,运行时无法应付;如果需要的话,系统应该能够每毫秒触发一次回调,但由于某种无法解释的原因,它通常不想这样做。
解决方案是使用准确的定时器,正如zer02评论的那样。不要被这个名字误导;所使用的机制与setTimeout()相同,但延迟会根据计时器触发前的剩余时间进行调整。因此,如果时间结束,"精确计时器"将调用setTimeout(callback,0),并立即运行。令人惊讶的是,系统负载小于setInterval():在我非常不科学的样本中,大约占CPU的2%,而不是5%。
这个简单的功能可能派上用场:
/**
* A high resolution timer.
*/
function timer(delay, callback)
{
// self-reference
var self = this;
// attributes
var counter = 0;
self.running = true;
var start = new Date().getTime();
/**
* Delayed running of the callback.
*/
function delayed()
{
callback(delay);
counter ++;
var diff = (new Date().getTime() - start) - counter * delay;
if (!self.running) return;
setTimeout(delayed, delay - diff);
}
// start timer
delayed();
setTimeout(delayed, delay);
}
要使用,只需调用new timer(delay, callback);
。(是的,我颠倒了参数的顺序,因为首先进行回调非常烦人。)要停止回调,请设置timer.running = false
。
最后一点:setTimeout(callback,delay)并没有像我担心的那样使用递归(比如:等待一段时间,然后调用回调),它只是将回调放在一个队列中,当轮到它时,运行时会在全局上下文中调用该队列。
我禁用了调试器并重试。它对我来说很好
- 如何从Java/scala调用js美化程序
- 是否可以在使用headerphp函数后自动调用JS函数
- 全局窗口热键在最小化chrome窗口时调用js函数
- 当用户点击动态创建的链接时,如何调用JS方法.JQuery,ASP.NET MVC
- 从PHP调用JS函数不起作用
- 如何从构造函数中调用js原型方法
- 从加载的页面调用js页面
- 在shtml中调用js函数
- 当调用JS函数具有alert()时,应用程序将被冻结
- 无法从onclick事件调用JS函数
- 如何在自动完成更新时调用JS函数
- 在单击浏览器选项卡上调用 js 函数
- 从 PHP 文件调用 js 函数
- 尝试在 PHP 代码中调用 js 函数
- 同时调用js函数yui
- 提交按钮的点击元素不调用JS函数
- 使用onclick和javascript函数,在参数中调用js-var
- 在另一个.js文件中调用js函数
- 使用grunt在angular指令中调用js文件
- 在JS文件中设置延迟以调用JS文件