【问题标题】:What is the right granularity of error catching in JavaScript?JavaScript 中错误捕获的正确粒度是多少?
【发布时间】:2012-12-28 08:03:21
【问题描述】:

我曾经在我的 JS 类的任何方法中放置一个 try...catch

var MyConstructor = function() {
    this.init = function() {
        try {
            // the method code...
        } catch(error) {
            // the error manager log actions
        }
    };
    // other methods, with the same try/catch usage
};

这样,保持代码接口相对简单,我认为我的代码中的任何错误都会被捕获并记录/管理。

var myInstance = new MyConstructor();

相反,每个脚本一个全局 catch 块就足够了吗? 关心抛出每个可能的(或值得注意的)错误,在我看来,知道应用程序中发生的每个错误就足够了:

// no try...catch inside the classes, but only a global try...catch per script:
try {
    var myInstance = new MyConstructor();
} catch(error) {
    /*
        log in the needed environment
        (e.g. client-side, via console, or to the server...)
    */
}

我搜索并阅读了 Stackoverflow 上的线程以及有关 JavaScript 错误管理的大量在线资源。
在这个阶段,我感兴趣的是找到找到所有错误的最佳方法,而不是管理它们以实现用户界面的优雅行为。
这不是正确的方法吗?我愿意接受任何建议。

【问题讨论】:

    标签: javascript error-handling try-catch


    【解决方案1】:

    经验法则是,您应该问自己“谁应该从逻辑上处理问题?”并坚持下去。

    重要的是要记住,当您编写一段代码时,您实际上是在编写一份描述代码片段如何交互的合约。例如,在您创建 MyConstructor 实例时,它为什么会失败?生成时做出了什么承诺?最重要的是,谁来处理它的失败?

    一些例子

    假设我们有一个类CarCar 的实例有一个方法drive(x)

    当您调用 drive(x) 时,Car 会将 x 位置向右移动。

    操作drive(x) 可能会失败,例如,如果Car 已经在屏幕边缘,或者Car 没有燃料。

    我们刚刚将 drive 定义为“汽车向右移动 x 个位置”,这意味着汽车期望能够完成驾驶动作而无法完成它在逻辑上是例外。在这种情况下,很明显处理异常的是驱动调用者而不是汽车本身,因为它不必知道它在什么环境下行驶。更好的是,呼叫者不应试图将汽车驶离边缘或以任何方式没有燃料。

    另一种情况

    假设在同一个示例中,Environment 是一个包含汽车的类,它有一个 moveCars() 方法,可以根据一些内部包含的逻辑移动环境中的所有汽车。 Environment 被期望它包含所有运动逻辑的程序使用。我们在moveCars() 中使用了一些算法,该算法应该向我们保证汽车不会发生碰撞。

    我们可能有一些在 moveCars() 方法中没有想到的边缘情况,或者由于某些假设不应该发生的情况,但是环境的用户希望它包含所有移动逻辑,这意味着当发生异常时应自行处理。

    那么,一般策略是什么?

    您应该根据运行代码的组件的职责来处理异常。

    【讨论】:

      【解决方案2】:

      这个问题实际上并不局限于 JavaScript。异常捕获的正确粒度取决于可能有​​异常的模块和函数的正确粒度。设计良好的模块或功能应该绑定到定义良好的合约(DbC)。只要明确建立契约,异常处理问题就会变得容易得多。

      关于合同的三个重要问题是:合同期望什么?合同保证什么?合约维护什么?例如,假设函数divide(a, b) 返回a / b。此函数要求b 非零(precondition),如果 b 恰好为零,则此函数应该抛出异常而不是 catch 异常。因为保证传递参数的有效性是调用者的责任,这是合同的一部分。除此之外,divide 函数中可能发生的所有其他错误应该 被捕获,因为这是它自己的责任。作为合约的另一部分,divide 承诺返回一个乘以b 的值(即商)应该等于a(称为postcondition)。

      异常处理可能很复杂。一个函数(或模块)可以选择在其职责范围内捕获异常,或者如果超出其职责则选择不捕获,或者选择先捕获异常,处理它,然后在包装异常后重新抛出给调用者作为调用层的异常。

      总而言之,异常处理不是一个独立的问题,它是整个系统设计的一部分。一旦设计者合理地将一个复杂的系统分成相对简单的模块,每个模块都有良好的抽象/接口和明确定义的合同/责任,如何以及在哪里处理异常应该是不言自明的。

      【讨论】:

        猜你喜欢
        • 2019-12-07
        • 2021-07-26
        • 2019-01-21
        • 1970-01-01
        • 1970-01-01
        • 2020-12-23
        • 2011-05-26
        • 2023-04-02
        • 2023-04-07
        相关资源
        最近更新 更多