56

这是一个非常基本的问题。在 Java 中,我使用 finally 语句来关闭资源,因为“这是一个好习惯”。几年来,我一直在使用 Javascript 和 Node.js 进行开发,但我从未使用过该finally语句。我知道在 Node.js 中,我们所有人都遵循第一个参数错误处理模式。无论如何,以下 2 个片段的作用相同:

try{
    throw 123
}catch (e){

}finally{
    console.log(1)
}

.

try{
    throw 123
}catch (e){

}

console.log(1)

两者都打印 1。

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

4

9 回答 9

50

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

只是一个简单明了的例子,显示了差异。有一个return中断函数完成,但是console.log在跳过finally最后一个时调用了 in。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();

于 2018-07-23T09:10:10.093 回答
33

但是试试这个:

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

console.log("quux")

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

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

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

foo() // "baz"
于 2018-10-16T23:47:22.550 回答
4

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

于 2013-08-15T10:51:40.843 回答
3

finally 块用于特殊目的。

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

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

于 2013-08-15T10:51:24.203 回答
2

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


编辑:

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

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

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

在这些情况下,如果在返回和第二个异常之后仍需要执行一些清理代码或任何其他代码,则可以将其放入 finally 块中,如果它要在 try 块之后和 catch 块之后执行.

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

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

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

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

于 2013-08-15T10:52:00.767 回答
1

当您想确保代码在最后执行时使用它,即使在执行期间出现异常:

InputStream is = new FileInputStream("C://test.txt");
try {
    //code...
} catch (Exception e) {
    //code...
} finally {
    is.close();
}
于 2013-08-15T10:55:18.700 回答
1

这个问题问得好。

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

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 中的异常一般有什么用处。

于 2013-08-15T11:45:14.513 回答
1

问题出在你的例子上。在某些情况下,您不想捕获异常。

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 也无法阻止它。

于 2020-01-20T11:17:49.543 回答
-1

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

于 2013-08-15T10:55:35.547 回答