跳转到内容

JS 中的 finally 语法有什么意义?是否多余?

写在finally里中的代码,与直接写在try...catch后面到底有什么区别?本文将对此进行详细讲解。

每一个开发者对try...catch语句应该都不会感到陌生,但是finally却不是每一个人都会用到。而且很多时候,无论我们代码写在finally里面还是外面,好像并没有什么太大区别。

比如,我们经常会使用的请求时的 loading 状态,有些人习惯在finally中重置 loading 状态。

示例 1:

js
let loading = false;

function fetchData() {
  loading = true;
  try {
    await fetch('/api/xxx');
  } finally {
    // 写在finally中
    loading = false;
  }
}

有些人习惯直接写在try...catch后面。

示例 2:

js
let loading = false;

function fetchData() {
  loading = true;
  try {
    await fetch('/api/xxx');
  } catch {
  }
  // 写在try...catch后面
  loading = false;
}

也有部分人甚至会分别在trycatch中各写一遍。

示例 3:

js
let loading = false;

function fetchData() {
  loading = true;
  try {
    await fetch('/api/xxx');
    // try中写
    loading = false;
  } catch {
    // catch中写
    loading = false;
  }
}

但无论哪种写法,对于当前的业务场景来说都是可行的。

因为finally的本意就是:无论try中的代码是否抛出异常,finally里面的代码“始终”都会执行

那么,直接写在try...catch语句后面的代码又何尝不是这样呢?没有异常时,代码自然是往下执行;有异常时,被catch捕获后,代码也是正常往下执行。

所以,finally的意义到底在哪里?

我们分两个角度来分析:

1️⃣ 示例 1 和示例 3

示例 3中分别在trycatch中都写了重置状态的代码,它是“完全”可以替代示例 1的,尤其是处理更复杂的业务逻辑时,比写在finally中更加灵活。对于示例 3的写法,finally的优势就是:能够减少相同逻辑的代码,同时增加代码的可读性

所以,示例 3可以看作是“原始的、粗暴的”写法,finally的出现简化了这种写法,同时让代码变得更优雅了。

当然,一些“特殊的”场景,finally也并不完全适用,还是需要根据具体场景具体分析。

2️⃣ 示例 1 和示例 2

示例 2将重置状态的代码放在了try...catch后面,虽然在示例中的最终效果是相同的,但示例 2的写法并不足够“安全”。

为什么说不足够“安全”?

因为try...catch后面的代码并不能保证“始终”会执行!它其实是与finally的定义是有所区别的,finally中的代码无论如何一定会执行。

比如这种情况:

js
function foo() {
  try {
    return 1;
  } finally {
    return 2;
  }
}

function bar() {
  try {
    return 1;
  } catch {}
  return 2;
}

foo();
bar();

分析一下,foo()bar()分别会返回什么?

如果你认真了解过finally,你就会知道: foo()会返回2bar()会返回1

从代码可以看出,foo()其实是有点反直觉的,明明在try中就已经return 1了,为什么最终返回的却是finally中的return 2呢?

但这也是合理的,还记得之前对finally的定义吗?finally中的代码“始终”都会执行!哪怕函数已经提前return了!这就是finally的绝对安全性。

除了returnthrowbreakcontinue都不能阻碍finally中语句的执行。

回到示例 2,如果在try中就提前return了,那么后面的重置代码将永远不会被执行。

js
let loading = false;

function fetchData() {
  loading = true;
  try {
    await fetch('/api/xxx');
    return; // 提前返回
  } catch {
  }
  // 写在try...catch后面 --- 永远不会被执行
  loading = false;
}

对于finally语句的意义,用自己的语言来一个小小的总结:

  • 减少相同逻辑代码
  • 使用更明确的语义化提高了代码的可读性
  • 保证“始终”要执行的代码的安全性

所以,finally只是 JS 给你提供的一个语法,用与不用都取决于你;没有必须使用它的场景,但也绝不是“多余”,它能够让你写代码时多一个更优的选择。