据我理解,(非集群) 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中)。其他回调(如net)也是如此。
所以我想
Error()有特殊的代码来隐藏这一点(作为不必要的实现细节)哪种(如果两者都是)是这样的,通常在nodejs (编辑:或v8)源代码中,我可以读取实际根事件循环的实现吗?
发布于 2013-08-22 20:22:57
答案(或至少一条线索)就在堆栈跟踪中。只需遵循堆栈底部文件中的代码即可。
我不确定您使用的是哪个版本的节点(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属性。
使用libuv表示的是一个C++模块。 ( 接口 );是C++代码( 调用 调用 ontimeout函数)。
这就是您的答案:堆栈的根是由JavaScript代码调用的C++函数(无论是计时器还是流管道)。
Error提供给您的堆栈跟踪没有显示调用函数所涉及的任何本机代码。事件循环本身是由V8 (本机代码)实现的,而不是由JavaScript实现的,因此您没有看到超出该边界的任何东西是有意义的。
所以正在发生的事情非常接近你的第一猜测。JavaScript代码通过将某些属性设置为函数(或在调用本机代码时将函数作为参数传递)来注册回调。当C++想调用该函数时,它将获取引用,并通过调用v8::Function::Call指示V8调用该函数。
如果您对V8的工作方式感兴趣,那么嵌入器指南是一个很好的开始。
https://stackoverflow.com/questions/18385572
复制相似问题