如何在 Javascript 中重新抛出异常,但保留堆栈?

问题描述 投票:0回答:5

在Javascript中,假设我想在异常发生时执行一些清理,但让异常继续在堆栈中传播,例如:

try {
  enterAwesomeMode();
  doRiskyStuff(); // might throw an exception
} catch (e) {
  leaveAwesomeMode();
  throw e;
}
doMoreStuff();
leaveAwesomeMode();

此代码的问题在于,捕获并重新抛出异常会导致该点之前的堆栈跟踪信息丢失,因此,如果随后再次捕获该异常(在堆栈的较高位置),堆栈跟踪只会下降到重新抛出。这很糟糕,因为这意味着它不包含实际引发异常的函数。

事实证明,至少在 Chrome 中,try..finally 具有相同的行为(也就是说,问题不在于重新抛出,而在于任何异常处理程序块的存在。)

有谁知道在 Javascript 中重新抛出异常但保留与其关联的堆栈跟踪的方法吗?如果做不到这一点,是否可以建议其他方法来添加异常安全的清理处理程序,同时在异常发生时捕获完整的堆栈跟踪?

感谢您的指点:)

javascript exception stack-trace callstack
5个回答
100
投票

这是 Chrome 中的一个错误。 重新抛出异常应该保留调用跟踪。

http://code.google.com/p/chromium/issues/detail?id=60240

我不知道有什么解决方法。

我没有看到最后的问题。 在某些情况下,我确实看到异常在finally之后没有默默地出现在错误控制台上,但这似乎在开发版本中得到了修复。


36
投票

Error 对象的 stack 属性是与 Error 对象本身同时创建的,而不是在抛出错误时创建的。 由于习语的原因,它们通常是相同的

 抛出新的错误(“消息”);

如果您按照编写的方式使用代码,那么当您重新抛出错误时,堆栈属性将不会发生更改。


8
投票

如前所述,堆栈是运行时创建的快照

new Error(...)
,因此您不能真正使用相同的堆栈抛出错误。

我使用的解决方法是在抛出之前

console.error
堆栈:

  console.error(err.stack);
  throw err;

它并不完美,但它为您提供了足够的可调试信息以及原始错误的堆栈。


0
投票

不幸的是,您必须手动执行此操作。我通常会创建自己的自定义错误类来执行此操作,并且无论我走到哪里都会随身携带它。如果您有一些全局错误处理程序,它最终会很有用,因为您可以检查这是否是您抛出的错误...我发现这在我构建的几乎每个应用程序中都很有用:

class AppError extends Error {
  constructor(messageOrError: string | Error) {
    const isError = messageOrError instanceof Error;
    const msg = isError ? messageOrError.message : messageOrError;
    super(msg);
    
    if (isError) {
      this.stack = messageOrError.stack;
      this.cause = messageOrError;
    }
  }
}

然后 - 如果需要,您可以检查 AppError:

if (err instanceof AppError) {
  // handle it differently than a generic erro
}

-3
投票

一开始就不要catch,直接用finally

try {
  enterAwesomeMode();
  doRiskyStuff(); // might throw an exception
  doMoreStuff();
} finally {
  leaveAwesomeMode();
}
© www.soinside.com 2019 - 2024. All rights reserved.