[英]Node.js: breakpoints in setInterval
我正在调试一个带有setInterval()
的node.js(Node版本6.2.1)程序。 代码很简单:
const log = console.log;
let cnt = 0;
const inc = () => {
const beforeDebug = Date.now();
log('Before debug time', beforeDebug);
debugger;
const afterDebug = Date.now();
log('After debug time', Date.now());
log('Time spent in debug', afterDebug - beforeDebug);
log(`[${cnt++}]`);
};
setInterval(inc, 1000);
```
我观察到的奇怪行为是setInterval()
回调执行之间的暂停将取决于我在断点中花费了多少时间。 例如,如果我在“调试器”行停留10秒然后恢复,我将在恢复后的 10秒内看到下一个数字。
我使用命令行节点调试器和节点检查器检查了此行为。
[UPDATE](为代码添加时间戳)这是命令行中提取的调试会话行:
Debugger listening on port 5858
connecting to 127.0.0.1:5858 ... ok
< Before debug time 1467952218915
< After debug time 1467952235018
< Time spent in debug 16103
< Before debug time 1467952252123
基本上,最后一节中回调执行的时间差是17105,这几乎完全是<time-in-debug> + <interval-value>
这个问题没有重大的实际影响,但我很想了解下面的计时器机制。 为什么它以这种奇怪的方式表现?
简短的回答是,用于确定何时触发回调的时间被缓存,并且可能与回调的实际插入时间不同步。
要理解为什么会发生这种情况,有助于了解节点的定时器是如何实现的( 在源代码中有很好的记录)。 在这里,我们需要记住的是:
setTimeout(foo, 10)
, foo
将被添加到超时10
的所有回调列表中。 insertionTime
让事情变得简单(这实际上是timer._idleStart
源)。 now
)并按顺序运行超时的回调,直到没有回调或回调的插入时间为now - insertionTime < timeout
。 在最后一种情况下,节点将延迟下一次超时唤醒直到timeout - (now - insertionTime)
。 在你的情况下,这是发生的事情:
t=0
, inc
插入一个insertTime insertionTime=0
的回调列表中。 t=1000
,超时唤醒, now=1000
记录now=1000
,并运行inc
。 t=3000
(或者你正在调试的时间长), inc
完成并重新插入回调列表 (因为你使用的是setInterval
),这次是setInterval
insertionTime=3000
。 t=3000
,节点继续浏览回调列表并找到新插入的回调。 它now - insertionTime = -2000
计算now - insertionTime = -2000
,小于timeout
(因为now
仍然等于1000
!)。 因此,它会安排下一次回调执行3000 = 1000 - (-2000)
毫秒。 t=6000
,超时再次唤醒, now=6000
记录now=6000
,并运行inc
。 您可以通过使用NODE_DEBUG=timer
运行程序来获得计时器内部的一些可见性:
$ NODE_DEBUG=timer node setinterval.js
TIMER 98831: no 1000 list was found in insert, creating a new one
TIMER 98831: timeout callback 1000
TIMER 98831: now: 1067
inc
TIMER 98831: 1000 list wait because diff is -2000
TIMER 98831: timeout callback 1000
TIMER 98831: now: 6072
inc
TIMER 98831: 1000 list wait because diff is -2000
...
据我所知,这看起来像节点中的一个错误。 我不知道什么时候有一个负diff
有意义的。
你似乎更多地出于好奇而要求这一点,但是如果你需要防止这种增加的延迟,你可以通过确保你至少有一个其他回调来解决相同的超时问题。 这将迫使now
进行更新。
function f() {
var end = Date.now() + 2000;
while (Date.now() < end); // 2 second busy loop.
console.log('f: ' + Date.now());
}
setInterval(f, 1000);
setTimeout(function () {
setInterval(function () {}, 1000);
}, 10); // Small delay to make sure they don't run in the same wakeup.
如果我们只添加f
,它最终会每5秒运行一次。 但是,如果我们也注册(空)第二个回调, f
将每3秒正确运行一次!
Node.js在一个线程中运行您的代码。 当您暂停执行时,相应的回调也将延迟,直到您恢复。
这是一个没有使用调试器的替代示例
var blockUntil = Date.now() + 7000;
function f() {
console.log('Before: ', Date.now())
while (Date.now() < blockUntil); // busy loop
console.log(' After: ', Date.now())
}
setInterval(f, 1000);
我可以确认它可以在没有调试器的情况下重现。
Before: 1467966150871
After: 1467966156847
Before: 1467966163851
After: 1467966163851
Before: 1467966164856
After: 1467966164856
Before: 1467966165859
After: 1467966165859
Before: 1467966166864
After: 1467966166864
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.