【问题标题】:Javascript is losing a backtrace in the catch blockJavascript 在 catch 块中丢失了回溯
【发布时间】:2020-07-08 16:45:53
【问题描述】:

以下代码示例:

async function expectResolutionErrorCode(object) {
  try {
    await Promise.resolve(1);
    object.f1() // caught error
  } catch (error) {
    object.f2() // uncaught error
  }
}

expectResolutionErrorCode(undefined).catch(err => console.log(err));

使用节点 v14 运行时产生以下回溯:

$ node  test.ts
TypeError: Cannot read property 'f2' of undefined
    at expectResolutionErrorCode (/Users/bogdan/makabu/unstoppable/resolution/test.ts:6:12)

如果我从示例中注释掉第 3 行的 await 语句,则回溯完成:

TypeError: Cannot read property 'f2' of undefined
    at expectResolutionErrorCode (/Users/bogdan/makabu/unstoppable/resolution/test.ts:6:12)
    at Object.<anonymous> (/Users/bogdan/makabu/unstoppable/resolution/test.ts:10:1)
    at Module._compile (internal/modules/cjs/loader.js:1201:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1221:10)
    at Module.load (internal/modules/cjs/loader.js:1050:32)
    at Function.Module._load (internal/modules/cjs/loader.js:938:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47

所以await 语句显然会导致节点丢失部分回溯。 为什么它会这样? 在try catch 中仍然使用await 语句的同时,是否有任何解决方法来维护回溯?

我在 Chrome 和 Safari 中测试了这种行为,堆栈跟踪以几乎相同的方式丢失:https://jsfiddle.net/61kdv8fx/

【问题讨论】:

  • 你在编译你的代码吗?对我来说,Node v14 可以很好地显示异步调用跟踪。
  • 旁注:您不需要在await Promise.resolve(1); 中使用Promise.resolve(当然它可能有助于强调),await 1; 的行为方式完全相同(根据规范)。 await 在其操作数上有效地调用了Promise.resolve,如果你给Promise.resolve 一个构造函数为Promise 的promise,它会返回相同的promise;它只包装非承诺值(包括来自 3rd 方库的 thenables)和由不同构造函数(例如子类)创建的承诺。

标签: javascript error-handling try-catch stack-trace


【解决方案1】:

这是因为堆栈已展开。请记住,await 是挂钩承诺结算的语法糖。因此,当您在代码中到达 await 时,函数会返回。 稍后,当代码继续执行时,因为 promise 已解决,堆栈很浅,因为直接调用了 promise 反应。

JavaScript 工具制造商非常意识到这一点,并致力于使用异步调用堆栈来解决这个问题。

但是,我应该注意,当我尝试复制您在 Node v14 上看到的内容时,我看不到它,因为 V8 已经有异步调用堆栈。示例:

async function example() {
    try {
        await Promise.resolve(1); // Could just be `await 1;`, but I wanted to match the question
        throw new Error("boom1");
    } catch (e) {
        throw new Error("boom2");
    }
}

async function a() {
    await b();
}

async function b() {
    await c()
}

async function c() {
    await example();
}

a();

当我运行它时:

$节点示例.js (节点:5342)UnhandledPromiseRejectionWarning:错误:boom2 例如 (/path/to/example.js:6:15) 在异步 c (/path/to/example.js:19:5) 在异步 b (/path/to/example.js:15:5) 在异步 (/path/to/example.js:11:5) (使用 `node --trace-warnings ...` 显示警告的创建位置) (节点:5342)UnhandledPromiseRejectionWarning:未处理的承诺拒绝。此错误源于在没有 catch 块的情况下抛出异步函数内部,或拒绝未使用 .catch() 处理的承诺。要在未处理的 Promise 拒绝时终止节点进程,请使用 CLI 标志 `--unhandled-rejections=strict`(请参阅 https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode)。 (拒绝编号:1) (节点:5342)[DEP0018] DeprecationWarning:不推荐使用未处理的承诺拒绝。将来,未处理的 Promise 拒绝将使用非零退出代码终止 Node.js 进程。

我在现代版本的 Chrome 和其他使用最新 V8 的浏览器上得到了相同的输出。但是 Firefox v78 仍然失去了上下文;我确信 SpiderMonkey 团队正在努力。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-16
    • 2014-09-27
    • 1970-01-01
    • 1970-01-01
    • 2012-07-08
    • 2019-07-28
    • 1970-01-01
    • 2017-02-18
    相关资源
    最近更新 更多