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

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

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

这段代码的问题在于,捕获和重新抛出异常会导致到该点为止的堆栈跟踪信息丢失,因此,如果随后再次捕获异常,在堆栈的上层,堆栈跟踪只会下降到重新抛出。这很糟糕,因为这意味着它不包含实际抛出异常的函数。

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

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

谢谢你的建议:)

74016 次浏览

Error 对象的堆栈属性是与 Error 对象本身同时创建的,而不是在它被抛出的时候创建的。因为成语的缘故,它们常常是一样的

throw new Error("message");

如果按照编写的方式使用代码,则在重新抛出错误时,堆栈属性将更改为 没有

这是 Chrome 中的一个 bug。重新引发一个异常应该会保留调用跟踪。

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

我不知道有什么办法。

我不觉得终于有什么问题。我确实看到在某些情况下,在 finally 之后,错误控制台上没有出现异常,但是在开发构建中,这个异常似乎是固定的。

正如前面提到的,堆栈是在运行 new Error(...)时创建的快照,因此实际上不能使用相同的堆栈抛出错误。

我使用的一个变通方法是在抛出之前先将堆栈设置为 console.error:

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

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

别一开始就抓住它,用 finally 就行了

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