[英]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.