首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么异常堆栈中的根条目因上下文不同而不同?

为什么异常堆栈中的根条目因上下文不同而不同?
EN

Stack Overflow用户
提问于 2013-08-22 16:03:09
回答 1查看 93关注 0票数 2

据我理解,(非集群) nodejs程序中的所有代码都在同一个线程中运行。考虑到这一点,我希望所有这样的代码都会作为同一个根事件循环的子代码运行,因此,如果我们检查堆栈跟踪中运行在不同回调中的代码,那么我们最终还是会返回到事件循环的同一个条目(“分派事件”行)。但事实并非如此,我也不明白为什么。

请考虑以下几点:

代码语言:javascript
复制
function printStackTrace() {
    console.log(new Error().stack);
}

printStackTrace();
setTimeout(printStackTrace, 1000);

运行这会产生:

代码语言:javascript
复制
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);仍然会给出一个不同的根:

代码语言:javascript
复制
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)也是如此。

所以我想

  • 事件循环是本机(C++)代码,因此它不会出现在堆栈跟踪和异步服务的基本提供者(repl.js、timers.js等)中。使用本机v8 api调用注册自己。
  • 事件循环是JavaScript,但是Error()有特殊的代码来隐藏这一点(作为不必要的实现细节)

哪种(如果两者都是)是这样的,通常在nodejs (编辑:或v8)源代码中,我可以读取实际根事件循环的实现吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-08-22 20:22:57

答案(或至少一条线索)就在堆栈跟踪中。只需遵循堆栈底部文件中的代码即可。

我不确定您使用的是哪个版本的节点(0.6?),但在最近的(0.10.17)中,

代码语言:javascript
复制
setTimeout(function() { console.log(new Error().stack) }, 1);

打印出来:

代码语言:javascript
复制
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的工作方式感兴趣,那么嵌入器指南是一个很好的开始。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18385572

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档