与在异步回调链中使用 try catch 块相比,返回异常有什么优势

What's the advantage of returning exceptions over using try catch blocks in async callback chain?

本文关键字:返回 异常 什么 catch 回调 异步 try      更新时间:2023-09-26

在查看了 Twisted 的 Deferred 类和 HeavyLifters 延迟库后,我注意到如果之前的结果值是 Failure 类的实例,则会触发错误。这让我思考:是否有任何特殊原因返回一个特殊对象来表示错误,而不仅仅是抛出错误。

根据我的推断,我觉得最好抛出错误,因为:

  1. 可以抛出任何值。
  2. 如果未捕获引发的值,则会向上传播调用堆栈。
  3. 不需要
  4. 特殊的FailureError类。
  5. 它使回调看起来更像同步代码。
  6. 可以在调用堆栈的任何级别处理异常。

我注意到的一些缺点是:

  1. 尝试代码块并捕获错误可能会导致代码性能下降。
  2. 如果未捕获异常,则它将停止执行回调链的其余部分。
  3. 异步编程是必不可少的,与使用 try catch 块截然相反。

我正在尝试权衡几率,并找出哪种报告错误的方法更适合这种情况。

Twisted 中的 Failure 类有许多方便的方法,使其非常有用,独立于Deferred中用于运行错误处理回调的功能。 浏览 API 文档,您会发现有用的方法,例如格式化回溯和检查异常类型。 Failure也是一个方便的地方,可以放置一些非常粗暴的黑客,以便与twisted.internet.defer.inlineCallbacks生成器以及生成微妙不同异常的 Cython 的特殊支持代码很好地集成。

Failure也是将回溯状态与异常一起保存的好地方。 Python 异常通常不携带堆栈信息,所以如果你只有一个异常,你无法找到它在哪里引发。 每当你想要处理捕获它的except块之外的异常时,这可能是一个主要缺点。

最后,能够返回Failure可以实现以下简单且通常有用的模式:

def handleOneErrorCase(reason):
    if not thisCaseApplies(reason):
        return reason
    handleThisCase(reason)
someDeferred.addErrback(handleOneErrorCase)

这经常出现在使用Deferred代码中。 它很方便,恰好在 CPython 上性能更高一些,并且还具有保留 reason 中的原始堆栈信息的优点(如果错误处理程序重新引发异常,此时的堆栈将替换原始堆栈,从而掩盖异常的原始原因)。

错误通常在异步环境中通过回调返回,因为无法在异步函数外部捕获错误。例如,在javascript中,如果您尝试:

try {
    setTimeout(function() { throw new Error(); }, 0);
} catch (e) {
    console.log('Caught it.');
}

然后异常将无法捕获。这与异步函数被重新注册,然后由不同堆栈中的事件循环调用有关,因此异常不会冒泡原始堆栈。