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

发布于 2024-09-19 19:28:08 字数 546 浏览 3 评论 0原文

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

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

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

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

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

感谢您的指点:)

In Javascript, suppose I want to perform some cleanup when an exception happens, but let the exception continue to propagate up the stack, eg:

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

The problem with this code is that catching and rethrowing the exception causes the stack trace information up to that point to be lost, so that if the exception is subsequently caught again, higher up on the stack, the stack trace only goes down to the re-throw. This sucks because it means it doesn't contain the function that actually threw the exception.

As it turns out, try..finally has the same behavior, in at least Chrome (that is, it is not the re-throw that is the problem precisely, but the presence of any exception handler block at all.)

Does anyone know of a way to rethrow an exception in Javascript but preserve the stack trace associated with it? Failing that, how about suggestions for other ways to add exception-safe cleanup handlers, while also capturing complete stack traces when an exception happens?

Thanks for any pointers :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

翻了热茶 2024-09-26 19:28:08

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

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

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

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

This is a bug in Chrome. Rethrowing an exception should preserve the call trace.

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

I don't know of any workaround.

I don't see the problem with finally. I do see exceptions silently not showing up on the error console in some cases after a finally, but that one seems to be fixed in development builds.

海夕 2024-09-26 19:28:08

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

   throw new Error("message");

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

The stack property of an Error object is created at the same time as the Error object itself, not at the point it's thrown. They're often the same because of the idiom

   throw new Error("message");

and if you use the code just as you've written it, the stack property will not be changed when you rethrow the error.

不气馁 2024-09-26 19:28:08

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

我使用的解决方法是在抛出之前console.error堆栈:

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

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

As mentioned, the stack is a snapshot created while running new Error(...), so you can't really throw the error with the same stack.

A workaround I used is to console.error the stack before throwing:

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

It's not perfect, but it gets you enough debug-able information, and the stack of the original error.

绝情姑娘 2024-09-26 19:28:08

我通常会创建自己的自定义错误类来执行此操作,并且无论我走到哪里都会随身携带它。它最终很有用,我在这些天构建的几乎每个应用程序中都使用它:

注意:这保留了原始的错误消息/堆栈。

class AppError extends Error {
  constructor(messageOrError: string | Error) {
    const isError = messageOrError instanceof Error;
    super(isError ? messageOrError.message : messageOrError);
    
    if (isError) {
      this.stack = messageOrError.stack;
      this.cause = messageOrError;
    }
    
    // you can do some logging here to save on boilerplate
    if (process.env.NODE_ENV === 'production') {
        log(this.message);
    } else {
        log(this.stack);
    }

  }
}

您可以像这样抛出原始错误/堆栈:

try {
  ...
} catch(err) {
  // do stuff
  throw new AppError(err);
}

您还可以检查此错误并以不同的方式处理它:

if (err instanceof AppError) {
  ...
}

I usually create my own custom error class for doing this and have just brought it around with me wherever I go. It ends up being useful and I use this in almost every app I build these days:

NOTE: this preserves the original error message/stack.

class AppError extends Error {
  constructor(messageOrError: string | Error) {
    const isError = messageOrError instanceof Error;
    super(isError ? messageOrError.message : messageOrError);
    
    if (isError) {
      this.stack = messageOrError.stack;
      this.cause = messageOrError;
    }
    
    // you can do some logging here to save on boilerplate
    if (process.env.NODE_ENV === 'production') {
        log(this.message);
    } else {
        log(this.stack);
    }

  }
}

You can throw the original error/stack like this:

try {
  ...
} catch(err) {
  // do stuff
  throw new AppError(err);
}

You can also check for this error and handle it differently:

if (err instanceof AppError) {
  ...
}
他不在意 2024-09-26 19:28:08

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

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

Don't catch it in the first place, just use finally

try {
  enterAwesomeMode();
  doRiskyStuff(); // might throw an exception
  doMoreStuff();
} finally {
  leaveAwesomeMode();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文