节点.js:setInterval 中的断点
Node.js: breakpoints in setInterval
我正在调试一个包含setInterval()
的节点.js(节点版本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 秒内看到下一个数字。
我使用命令行节点调试器和节点检查器检查了此行为。
[更新](在代码中添加时间戳)下面是命令行中调试会话的提取行:
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
入到带有insertionTime=0
的回调列表中。 -
t=1000
,超时唤醒,记录now=1000
,并运行inc
。 -
t=3000
(或调试多长时间),inc
完成并重新插入回调列表中(因为您使用的是setInterval
),这次使用insertionTime=3000
。 -
t=3000
,节点继续浏览回调列表并找到新插入的回调。它计算小于timeout
的now - insertionTime = -2000
(因为now
仍然等于1000
!因此,它会在3000 = 1000 - (-2000)
毫秒后安排下一次回调执行。 -
t=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 秒正确运行一次!
下面是一个不使用调试器的替代示例
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
- node.js:setInterval()正在跳过调用
- 触发媒体查询断点时刷新页面
- setInterval和clearInterval javascript无法按需工作
- Don'我不明白为什么我的setInterval+jQuery;不起作用
- setInterval游戏循环的范围问题
- JavaScript setInterval problems
- 如何在Javascript中延迟setInterval
- 如何执行相同的setInterval()
- SetInterval在javascript对象中表现怪异
- 从setInterval方法移动到带有sprite的requestAnimationFrame
- 如何在悬停时暂停setInterval
- 可以't获取setInterval函数以使用javascript中的this.function_name调用另一
- 删除对象时终止setInterval
- 使用setInterval()函数进行图像闪烁
- “;对于“;内部”;setInterval”;
- 能够在指向本地环境的浏览器中设置JavaScript断点
- 在setInterval函数之前声明时未定义对象
- Firebug错误:setInterval()从断点继续后回调停止
- 使用setinterval不断点击Id
- 节点.js:setInterval 中的断点