Promise.prototype.finally()
Promise.prototype.finally() 的基本概念
ECMAScript 2018(ES9)引入了Promise.prototype.finally()
方法,它允许在Promise无论成功还是失败后都执行指定的回调函数。这个方法解决了之前需要在then()
和catch()
中重复相同代码的问题。
finally()
方法接收一个回调函数作为参数,这个回调函数不接受任何参数,因为它不关心Promise的最终状态是fulfilled还是rejected。
const promise = new Promise((resolve, reject) => {
// 异步操作
});
promise
.then(result => { /* 处理成功 */ })
.catch(error => { /* 处理失败 */ })
.finally(() => { /* 无论成功失败都会执行 */ });
finally() 的行为特点
finally()
方法有几个重要的行为特点值得注意:
- 回调函数不接收任何参数,因为它不关心Promise的最终状态
- 返回一个新的Promise,其值与原Promise相同
- 如果回调函数抛出错误或返回一个rejected Promise,则会传递这个错误
- 在Promise链中,finally()不会改变原Promise的值
Promise.resolve(2)
.then(x => x * 2)
.finally(() => { console.log('计算完成') })
.then(x => console.log(x)); // 输出: 计算完成 然后 4
Promise.reject(new Error('失败'))
.catch(e => { console.log(e.message); return '恢复值' })
.finally(() => console.log('清理工作'))
.then(x => console.log(x)); // 输出: 失败 清理工作 恢复值
与then/catch的区别
finally()
与then()
和catch()
有几个关键区别:
- 参数处理:
then()
和catch()
回调接收Promise的结果或错误,而finally()
不接收任何参数 - 返回值影响:
finally()
回调的返回值不会影响Promise链,除非抛出错误 - 执行时机:
finally()
确保在Promise完成后执行,无论成功或失败
// then/catch需要重复代码
somePromise
.then(result => {
doSomething(result);
cleanup();
})
.catch(error => {
handleError(error);
cleanup();
});
// 使用finally更简洁
somePromise
.then(result => doSomething(result))
.catch(error => handleError(error))
.finally(() => cleanup());
实际应用场景
finally()
在多种场景下非常有用:
- 资源清理:无论操作成功与否都需要执行的清理工作
- 加载状态管理:隐藏加载指示器
- 数据库连接:关闭数据库连接
- 动画控制:停止加载动画
let isLoading = true;
fetch('/api/data')
.then(response => response.json())
.then(data => processData(data))
.catch(error => showError(error))
.finally(() => {
isLoading = false;
document.getElementById('loading').style.display = 'none';
});
实现原理与兼容性
finally()
的实现可以看作是一个特殊的then()
调用:
Promise.prototype.finally = function(callback) {
return this.then(
value => Promise.resolve(callback()).then(() => value),
reason => Promise.resolve(callback()).then(() => { throw reason })
);
};
在兼容性方面,现代浏览器和Node.js都支持finally()
,但对于旧环境可能需要polyfill。可以使用core-js或es6-promise等库来提供支持。
常见陷阱与注意事项
使用finally()
时需要注意以下几点:
- 回调函数中的错误:如果在
finally()
回调中抛出错误,会覆盖之前的Promise状态 - 返回值:
finally()
回调的返回值会被忽略,除非返回一个rejected Promise - 异步清理:如果清理工作需要异步完成,应该返回一个Promise
// 错误示例:finally中的错误会覆盖原有结果
Promise.resolve('成功')
.finally(() => { throw new Error('finally错误') })
.catch(e => console.log(e.message)); // 输出: finally错误
// 正确处理异步清理
somePromise
.finally(() => {
return asyncCleanup(); // 返回Promise确保异步清理完成
});
与其他异步特性的结合
finally()
可以很好地与其他异步特性结合使用:
- async/await:在async函数中使用
finally
- Promise.all:与
Promise.all()
等组合方法一起使用 - 可取消Promise:在取消逻辑中使用
finally
进行清理
async function fetchData() {
try {
const response = await fetch('/api/data');
return await response.json();
} catch (error) {
console.error('获取数据失败:', error);
throw error;
} finally {
console.log('请求完成');
}
}
// 与Promise.all结合
Promise.all([promise1, promise2])
.then(results => processResults(results))
.finally(() => console.log('所有操作完成'));
性能考虑
虽然finally()
提供了便利,但在性能敏感的场景中需要考虑:
- 微任务队列:
finally()
会增加微任务队列的长度 - 内存使用:长Promise链中的多个
finally()
会增加内存开销 - 错误处理:额外的
finally()
回调可能增加错误处理复杂度
// 避免不必要的finally链
// 不推荐
promise
.finally(() => {})
.finally(() => {})
.finally(() => {});
// 推荐
promise.finally(() => {
// 合并所有清理逻辑
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:数据验证与输入过滤