【问题标题】:Uses of the finally statementfinally 语句的使用
【发布时间】:2013-08-15 10:48:59
【问题描述】:

这是一个非常基本的问题。在 Java 中,我使用 finally 语句来关闭资源,因为“这是一个好习惯”。几年来,我一直在使用 Javascript 和 Node.js 进行开发,并且我从未使用过 finally 语句。我知道在 Node.js 中,我们所有人都遵循 first parameter error handling 模式。无论如何,以下 2 个 sn-ps 也是如此:

try{
    throw 123
}catch (e){

}finally{
    console.log(1)
}

.

try{
    throw 123
}catch (e){

}

console.log(1)

都打印 1。

如果finally 没有真正的好处,为什么它是关键字?清理代码可以放在catch里面。

【问题讨论】:

  • 我不相信这是重复的。链接的问题被标记为 Java,而这个问题是关于 JavaScript 的。答案恰好是相同的,但将其标记为重复表明 Java 和 JavaScript 是相同的,但它们显然不是。
  • "清理代码可以放在 catch 中。" - 不可以。清理代码必须在try 块中的代码成功完成时和当它抛出。

标签: javascript node.js


【解决方案1】:

finally 不仅仅用于异常处理——它允许程序员避免清理代码被 return、continue 或 break 意外绕过。

只是一个简单明了的例子来说明差异。有一个 return 会中断函数完成,但是会调用 finally 中的 console.log 而最后一个 console.log 会被跳过。

let letsTry = () => {

  try {
    // there is a SyntaxError
    eval('alert("Hello world)');
    
  } catch(error) {
    console.error(error);
	
    // break the function completion
    return;
  } finally {
      console.log('finally')
  }

  // This line will never get executed
  console.log('after try catch')
}

letsTry();

【讨论】:

    【解决方案2】:

    但是试试这个:

    try {
        throw "foo"
    } catch (e) {
        throw "bar"
    } finally {
        console.log("baz")
    }
    
    console.log("quux")
    

    如果在 catch 块内引发第二个错误,try...catch 块之后的代码将不会运行。
    finally 块将始终运行,即使 catch 块中存在错误。

    此外,即使returnbreak 语句停止trycatch 块中的代码,finally 块也会运行。 finally 块中的 return 语句覆盖 trycatch 块中的 return 语句。

    function foo() {
        try {
            return "bar";
        } finally {
            return "baz";
        }
    }
    
    foo() // "baz"
    

    【讨论】:

      【解决方案3】:

      oracle docs 对此提供了一个很好的答案。底线:终于总是被调用!即使您只捕获一种异常(不是全局捕获),最终也会被调用(之后如果没有其他捕获,您的应用程序可能会中断)

      【讨论】:

      • 你能用javascript做吗?
      • Oracle 文档在这里无关紧要。这是一个 JavaScript 问题,而不是 Java 问题。目前,您不能在 Try 语句中使用多个 catch,因此您捕获了多少种异常是无关紧要的。我不认为“全局捕获”在 JS 中是一回事。
      【解决方案4】:

      the finally block 用于特殊用途。

      finally 不仅仅用于异常处理——它允许程序员避免清理代码被 return、continue 或 break 意外绕过。将清理代码放在 finally 块中始终是一种很好的做法,即使没有预料到异常也是如此。

      因为它不会影响你的业务逻辑,它仍然是编译器友好的,在内存方面。

      【讨论】:

        【解决方案5】:

        如果 try-block 提前返回或抛出您无法处理的异常怎么办?你仍然想释放你分配的资源,对吧?


        编辑:

        这个问题的答案似乎很哲学,有一些“猜测”,基本上是“我们相信它应该有用,因为它就在那里,所以它应该有用”,“甚至甲骨文都这么说”。或者它可以帮助程序员不要“忘记某些东西”或“意外退出而没有意识到”。

        这些几乎都是正当理由,但也有技术原因。

        它有助于避免上述情况下的代码重复,其中 (a) try 或其中一个 catch 块返回,或 (b) 如果在 catch 块内引发第二个异常。

        在这些情况下,如果某些清理代码或任何其他在 return 和第二个异常之后仍需要执行的代码,可以放在 finally 块中,如果它要在 try 之后和 after 之后执行catch 块。

        您仍然可以在没有 finally 块的情况下执行此操作,但必须复制代码,finally 块允许您避免这种情况。这是你真正需要的地方。

        因此,如果您确定不会错过 (a) 或 (b) 的情况,您仍然可以将“finally”代码放在 try/catch 块之后并省略 finally 子句。

        但如果情况发生变化怎么办?当您或其他人稍后更改代码时,可能会忘记检查清理代码现在是否在某些情况下被跳过。

        那么为什么不总是将清理代码放在 finally 块中呢?这是推荐的,也是许多 JavaScript 程序员所做的。

        【讨论】:

          【解决方案6】:

          当你想确保你的代码在最后执行时使用它,即使在执行过程中出现异常:

          InputStream is = new FileInputStream("C://test.txt");
          try {
              //code...
          } catch (Exception e) {
              //code...
          } finally {
              is.close();
          }
          

          【讨论】:

          • 几乎,如果你在catch块中返回一些东西,它也会执行
          • 这并不能回答问题,甚至根本与 Javascript 无关
          【解决方案7】:

          这是一个很好的问题。

          几乎没有理由在 javascript 中使用 finally,但我可以想象它可以实际使用的情况。

          假设您有一个网页,在某些用户操作后显示某个 div,例如点击按钮。
          div 显示了一些日志记录,例如用户请求的操作。
          操作完成后(错误或无错误),您要确保再次隐藏div。为此,您可以使用finally 子句。

          function doSomething() {
              var d = document.getElementById("log");
              show(d);
              try {
                  ... execute action ...
              } catch(e) {
                  log(e);
              } finally {
                  hide(d);
              }
          }
          

          一般来说,正如您所提到的,在 JavaScript 中越来越少使用异常来支持错误回调。
          所以,不妨问一下,JavaScript 中的异常一般有什么用处。

          【讨论】:

          • 在您的示例中,为什么不在catch 块之后调用hide(d),而不是在finally 内。无论哪种方式,代码都会运行try/catch,然后运行hide(d),所以使用finally似乎没有任何意义?
          【解决方案8】:

          问题在于您的示例。有些情况下您不想捕获异常。

          try {
              if (Math.random() > 0.5) throw 123
          }
          finally {
              console.log(1)
          }
          

          在这些情况下,如果您不想使用 finally,您所能做的就是重新抛出异常。

          try {
              if (Math.random() > 0.5) throw 123
          }
          catch (e) {
              console.log(1)
              throw e
          }
          console.log(1)
          

          或许

          try {
              if (Math.random() > 0.5) throw 123
              console.log(1)
          }
          catch (e) {
              console.log(1)
              throw e
          }
          

          这两种替代解决方案都会导致代码重复,这就是您需要 finally 关键字的原因。它大部分时间用于释放未使用的资源。忘记它可能会导致不需要的锁或连接或内存泄漏。我想在某些情况下,即使是智能 GC 也无法阻止它。

          【讨论】:

            【解决方案9】:

            在 Java 中,如果抛出了与任何 catch 块都不匹配的异常,则执行将中断,并且所有打开的资源都将保持打开状态。 finally 块将始终执行,即使发生未捕获的异常。

            【讨论】:

            • 这个问题是关于JavaScript的
            猜你喜欢
            • 1970-01-01
            • 2010-11-25
            • 2013-06-04
            • 2013-05-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-05-11
            • 2014-04-19
            相关资源
            最近更新 更多