2013-08-22 43 views
2

我的理解是(非集羣)nodejs程序中的所有代碼都運行在同一個線程中。鑑於此,我期望所有這樣的代碼將作爲同一個根事件循環的子代運行,因此,如果我們檢查了在不同回調中運行的代碼的堆棧跟蹤,我們最終還是會回溯到相同的條目(該事件循環的「調度事件」行)。但事實並非如此,我不明白爲什麼。爲什麼異常堆棧中的根條目根據上下文而有所不同?

考慮以下幾點:

function printStackTrace() { 
    console.log(new Error().stack); 
} 

printStackTrace(); 
setTimeout(printStackTrace, 1000); 

運行率:

Error 
    at printStackTrace (/tmp/node/test.js:4:17) 
    at Object.<anonymous> (/tmp/node/test.js:7:1) 
    at Module._compile (module.js:446:26) 
    at Object..js (module.js:464:10) 
    at Module.load (module.js:353:32) 
    at Function._load (module.js:311:12) 
    at Array.0 (module.js:484:10) 
    at EventEmitter._tickCallback (node.js:190:39) 
Error 
    at Object.printStackTrace [as _onTimeout] (/tmp/node/test.js:4:17) 
    at Timer.ontimeout (timers.js:94:19) 

,只是在REPL運行console.log(new Error().stack);給出了不同的根還是:

Error 
    at repl:1:13 
    at REPLServer.eval (repl.js:80:21) 
    at repl.js:190:20 
    at REPLServer.eval (repl.js:87:5) 
    at Interface.<anonymous> (repl.js:182:12) 
    at Interface.emit (events.js:67:17) 
    at Interface._onLine (readline.js:162:10) 
    at Interface._line (readline.js:426:8) 
    at Interface._ttyWrite (readline.js:603:14) 
    at ReadStream.<anonymous> (readline.js:82:12) 

所以根(最底層)項目不同(分別在EventEmitter,Timer和ReadStream中) ELY)。其他回調(例如網絡)也是如此。

所以我想,無論是

  • 事件循環是本機(C++)代碼,因此它不會在堆棧跟蹤顯示,和異步服務(repl.js,定時器的基本供應。 js等)使用本地v8 api調用向其註冊自己。
  • 事件循環爲JavaScript,但Error()具有特殊的代碼隱藏這個(是不必要的實現細節)

哪個(如果任一)的這些情況下,和一般地方在的NodeJS(編輯:或v8)源可以讀取真正的根事件循環的實現嗎?

+0

您可能希望將此問題的鏈接發送到freenode IRC上的#node.js頻道。 –

回答

1

答案(或至少一個線索)就在堆棧跟蹤中。只需按照堆棧底部文件中的代碼。

我不知道正是你使用的是什麼版本的節點(0.6時間更新?!),但在最新的(0.10.17),

setTimeout(function() { console.log(new Error().stack) }, 1); 

打印出:

Error 
    at null._onTimeout (repl:1:38) 
    at Timer.listOnTimeout [as ontimeout] (timers.js:110:15) 

那我們來看看timers.js:110。此行生存在listOnTimeout函數中,該函數被分配給Timer實例的ontimeout屬性。

A Timer is a C++ moduleinterfaces與libuv;它是C++代碼invokesontimeout函數。

所以你的答案是:堆棧的根是C++代碼調用的JavaScript函數(不管是定時器還是流管道)。

Error向您提供的堆棧跟蹤不會顯示任何涉及調用函數的本機代碼。事件循環本身是由V8(本地代碼)實現的,而不是JavaScript,所以它應該是有道理的,你沒有看到超出邊界的任何東西。

所以發生的事情非常接近你的第一個猜測。 JavaScript代碼通過將某些屬性設置爲函數(或將函數作爲參數在調用本機代碼中)來註冊回調。當C++想要調用該函數時,它會獲取參考並通過調用v8::Function::Call來指示V8調用該函數。

如果您對V8的工作方式感興趣,embedder's guide是一個好開始。

相關問題