簡體   English   中英

Node.js:setInterval中的斷點

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

在你的情況下,這是發生的事情:

  1. t=0inc插入一個insertTime insertionTime=0的回調列表中。
  2. t=1000 ,超時喚醒, now=1000記錄now=1000 ,並運行inc
  3. t=3000 (或者你正在調試的時間長), inc完成並重新插入回調列表 (因為你使用的是setInterval ),這次是setInterval insertionTime=3000
  4. t=3000 ,節點繼續瀏覽回調列表並找到新插入的回調。 now - insertionTime = -2000計算now - insertionTime = -2000 ,小於timeout (因為now仍然等於1000 !)。 因此,它會安排下一次回調執行3000 = 1000 - (-2000)毫秒。
  5. t=6000 ,超時再次喚醒, now=6000記錄now=6000 ,並運行inc
  6. ...

您可以通過使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM