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

207
在JavaScript中,假设我想在异常发生时执行一些清理操作,但是让异常继续向上传播,例如:
try {
  enterAwesomeMode();
  doRiskyStuff(); // might throw an exception
} catch (e) {
  leaveAwesomeMode();
  throw e;
}
doMoreStuff();
leaveAwesomeMode();
这段代码的问题在于捕获并重新抛出异常会导致到那个点为止的堆栈跟踪信息丢失,因此如果异常随后被再次高层次地捕获,堆栈跟踪仅向下到达重新抛出语句。这很糟糕,因为它意味着它不包含实际引发异常的函数。
事实证明,在至少Chrome中,try..finally具有相同的行为(也就是说,不是重新抛出异常导致问题,而是存在任何异常处理程序块)。
有人知道如何在JavaScript中重新抛出异常但保留与其关联的堆栈跟踪吗?如果失败,你有没有其他建议以添加异常安全清理处理程序,同时在发生异常时捕获完整的堆栈跟踪?
感谢任何指针 :)

复制:https://dev59.com/7FXTa4cB1Zd3GeqPyA47 - MatthewMartin
4个回答

96

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

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

我不知道有任何解决方法。

我没有看到finally有什么问题。我确实看到在某些情况下,在finally之后异常静默地不显示在错误控制台上,但这个问题似乎已经在开发版本中得到修复。


13
这个问题已经被关闭。 - Zachary Burns
1
截至Chrome 92,该问题仍然存在。error.stack 反映的是重新抛出的行号,而不是原始行号。 - Gillespie

35

一个 Error 对象的 stack 属性是在创建 Error 对象本身的同时创建的,而不是在抛出错误时创建的。它们通常是相同的,因为有一种约定俗成的用法:

   throw new Error("message");

如果您按照刚刚编写的代码使用它,那么当您重新抛出该错误时,stack 属性将不会更改。


7
这并不是真的(可能与平台有关)。我现在使用的js引擎(Rhino)在抛出异常语句时会重置堆栈,导致原始堆栈丢失。 - Ted Bigham
1
也许是这样,但是rhino-1.7.7.2.jar并没有改变它。你使用的是哪个版本? - Mike Stay

6

如前所述,堆栈是在运行 new Error(...) 时创建的快照,因此您无法使用相同的堆栈抛出错误。

我使用的一种变通方法是在抛出之前console.error 堆栈:

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

虽然不是完美的,但可以提供足够的调试信息和原始错误的堆栈信息。


-3

不要在第一时间捕获它,直接使用finally

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

2
如果您需要在catch部分中进行某些条件处理,则可能无法选择该选项,例如,检查某些条件并处理它,否则重新抛出相同的异常。 - Jan Hansen
1
或者你想记录错误但继续执行。 - zumalifeguard
问题是如何重新抛出异常并保留调用堆栈。如果您正在记录日志并继续执行,则无需考虑这些内容。而且,如果您想在catch中添加一些棘手的逻辑,那么这可能不适用,但这不是原始问题的一部分。 - texuf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接