在 JavaScript 中,finally 是 try/catch/finally 语句的一部分,其核心特性是 无论 try 代码块正常执行、catch 捕获到错误,还是执行过程中遇到 return/throw,finally 都会执行。其执行时期的核心规则的是:
一、核心执行时机
finally 的执行时期介于 try/catch 代码块执行完毕后 和 当前函数返回结果 / 后续代码执行前。
简单说:try 或 catch 执行完(无论成功还是失败),都会先进入 finally,finally 执行完后,才会继续执行后续逻辑(如函数返回、外层代码)。
二、分场景详细说明(附示例)
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 中 return,finally 无 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 中 return,finally 无 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 同步代码执行,不会等待异步操作(如 setTimeout、Promise)完成。
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 的执行时期可概括为:
- 同步场景:
try/catch执行完毕后 →finally执行 → 函数返回 / 后续代码执行; - 异步场景(Promise):等待
then/catch完成后执行; - 不受
try成功 / 失败、try/catch中return的影响(除非进程终止); - 若
finally中有return,会覆盖try/catch中的返回值。