在Javascript中,假设我想在异常发生时执行一些清理,但让异常继续在堆栈中传播,例如:
try {
enterAwesomeMode();
doRiskyStuff(); // might throw an exception
} catch (e) {
leaveAwesomeMode();
throw e;
}
doMoreStuff();
leaveAwesomeMode();
此代码的问题在于,捕获并重新抛出异常会导致该点之前的堆栈跟踪信息丢失,因此,如果随后再次捕获该异常(在堆栈的较高位置),堆栈跟踪只会下降到重新抛出。这很糟糕,因为这意味着它不包含实际引发异常的函数。
事实证明,至少在 Chrome 中,try..finally 具有相同的行为(也就是说,问题不在于重新抛出,而在于任何异常处理程序块的存在。)
有谁知道在 Javascript 中重新抛出异常但保留与其关联的堆栈跟踪的方法吗?如果做不到这一点,是否可以建议其他方法来添加异常安全的清理处理程序,同时在异常发生时捕获完整的堆栈跟踪?
感谢您的指点:)
这是 Chrome 中的一个错误。 重新抛出异常应该保留调用跟踪。
http://code.google.com/p/chromium/issues/detail?id=60240
我不知道有什么解决方法。
我没有看到最后的问题。 在某些情况下,我确实看到异常在finally之后没有默默地出现在错误控制台上,但这似乎在开发版本中得到了修复。
Error 对象的 stack 属性是与 Error 对象本身同时创建的,而不是在抛出错误时创建的。 由于习语的原因,它们通常是相同的
抛出新的错误(“消息”);
如果您按照编写的方式使用代码,那么当您重新抛出错误时,堆栈属性将不会发生更改。
如前所述,堆栈是运行时创建的快照
new Error(...)
,因此您不能真正使用相同的堆栈抛出错误。
我使用的解决方法是在抛出之前
console.error
堆栈:
console.error(err.stack);
throw err;
它并不完美,但它为您提供了足够的可调试信息以及原始错误的堆栈。
不幸的是,您必须手动执行此操作。我通常会创建自己的自定义错误类来执行此操作,并且无论我走到哪里都会随身携带它。如果您有一些全局错误处理程序,它最终会很有用,因为您可以检查这是否是您抛出的错误...我发现这在我构建的几乎每个应用程序中都很有用:
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
}
一开始就不要catch,直接用finally
try {
enterAwesomeMode();
doRiskyStuff(); // might throw an exception
doMoreStuff();
} finally {
leaveAwesomeMode();
}