你真的知道finally的执行时期吗????

820 阅读4分钟

在 JavaScript 中,finally 是 try/catch/finally 语句的一部分,其核心特性是 无论 try 代码块正常执行、catch 捕获到错误,还是执行过程中遇到 return/throwfinally 都会执行。其执行时期的核心规则的是:

一、核心执行时机

finally 的执行时期介于 try/catch 代码块执行完毕后 和 当前函数返回结果 / 后续代码执行前

简单说:try 或 catch 执行完(无论成功还是失败),都会先进入 finallyfinally 执行完后,才会继续执行后续逻辑(如函数返回、外层代码)。

二、分场景详细说明(附示例)

1. 场景 1:try 无错误(正常执行)

执行顺序:try 代码块 → finally 代码块 → 后续代码

javascript

运行

try {
  console.log("try 执行"); // 第一步:执行
} catch (err) {
  console.log("catch 执行"); // 不执行(无错误)
} finally {
  console.log("finally 执行"); // 第二步:执行(try后必走)
}
console.log("后续代码"); // 第三步:执行(finally后)

// 输出顺序:
// try 执行
// finally 执行
// 后续代码

2. 场景 2:try 有错误,且被 catch 捕获

执行顺序:try 报错中断 → catch 处理 → finally 执行 → 后续代码

javascript

运行

try {
  console.log("try 执行"); // 第一步:执行
  throw new Error("手动报错"); // 第二步:报错,中断try
  console.log("try 后续代码"); // 不执行(已中断)
} catch (err) {
  console.log("catch 处理错误:", err.message); // 第三步:处理错误
} finally {
  console.log("finally 执行"); // 第四步:必执行
}
console.log("后续代码"); // 第五步:执行

// 输出顺序:
// try 执行
// catch 处理错误:手动报错
// finally 执行
// 后续代码

3. 场景 3:try 有错误,未被 catch 捕获(无 catch 或 catch 不匹配)

执行顺序:try 报错中断 → finally 执行 → 错误抛出给上层(后续代码不执行)

javascript

运行

try {
  console.log("try 执行"); // 第一步:执行
  throw new Error("未被捕获的错误"); // 第二步:报错
} finally {
  console.log("finally 执行"); // 第三步:必执行(即使报错未捕获)
}
console.log("后续代码"); // 不执行(错误已抛出,中断执行)

// 输出顺序:
// try 执行
// finally 执行
// Uncaught Error: 未被捕获的错误(控制台报错)

4. 场景 4:try/catch 中包含 return(关键坑点)

核心规则try/catch 中的 return 会先「暂存返回值」,然后执行 finally,最后再返回暂存的值。finally 不会阻止 return,但如果 finally 中也有 return,会覆盖之前暂存的返回值。

示例 4.1:try 中 returnfinally 无 return

javascript

运行

function fn() {
  try {
    console.log("try 执行");
    return 100; // 第一步:暂存返回值 100,不立即返回
  } finally {
    console.log("finally 执行"); // 第二步:执行(在return前)
  }
}

console.log(fn()); // 第三步:返回暂存的 100

// 输出顺序:
// try 执行
// finally 执行
// 100
示例 4.2:catch 中 returnfinally 无 return

javascript

运行

function fn() {
  try {
    throw new Error("报错");
  } catch (err) {
    console.log("catch 执行");
    return 200; // 暂存返回值 200
  } finally {
    console.log("finally 执行"); // 执行后才返回 200
  }
}

console.log(fn()); // 200

// 输出顺序:
// catch 执行
// finally 执行
// 200
示例 4.3:finally 中包含 return(覆盖之前的返回值)

javascript

运行

function fn() {
  try {
    return 100; // 暂存 100
  } finally {
    return 300; // 直接返回 300,覆盖之前的 100
  }
}

console.log(fn()); // 300(不是 100)

5. 场景 5:异步代码中的 finally(易混淆点)

finally 是 同步执行 的,仅跟随其所属的 try/catch 同步代码执行,不会等待异步操作(如 setTimeoutPromise)完成。

javascript

运行

try {
  console.log("try 同步执行");
  setTimeout(() => {
    console.log("try 中的异步代码"); // 异步,后续执行
  }, 0);
} finally {
  console.log("finally 同步执行"); // 紧跟 try 同步代码执行
}

// 输出顺序:
// try 同步执行
// finally 同步执行
// try 中的异步代码(一段时间后)

如果是 Promise 中的 finally(ES2018 新增),则会等待 Promise 状态变更后执行(异步 finally):

javascript

运行

Promise.resolve("成功")
  .then(res => console.log("then:", res))
  .catch(err => console.log("catch:", err))
  .finally(() => console.log("finally 执行")); // 等待 then 完成后执行

// 输出顺序:
// then: 成功
// finally 执行

三、特殊例外:无法执行 finally 的情况

只有当进程被强制终止时,finally 才不会执行(极端情况):

  • 调用 process.exit()(Node.js):直接终止进程,跳过 finally
  • 浏览器崩溃、电脑断电等硬件 / 环境异常。

javascript

运行

try {
  console.log("try 执行");
  process.exit(0); // Node.js 中强制退出进程
} finally {
  console.log("finally 执行"); // 不执行(进程已终止)
}

四、总结

finally 的执行时期可概括为:

  1. 同步场景:try/catch 执行完毕后 → finally 执行 → 函数返回 / 后续代码执行;
  2. 异步场景(Promise):等待 then/catch 完成后执行;
  3. 不受 try 成功 / 失败、try/catch 中 return 的影响(除非进程终止);
  4. 若 finally 中有 return,会覆盖 try/catch 中的返回值。