Promise的finally()方法
Promise.prototype.finally() 的基本概念
ECMAScript 2018引入的finally()
方法为Promise链式调用提供了统一处理完成/拒绝状态的入口。无论Promise最终是fulfilled还是rejected状态,finally()
中的回调函数都会被执行。这个方法的主要用途是执行清理操作,比如关闭数据库连接、隐藏加载动画等。
fetch('/api/data')
.then(response => response.json())
.catch(error => console.error('Error:', error))
.finally(() => {
console.log('请求结束,无论成功或失败');
hideLoadingSpinner();
});
finally() 的语法特性
finally()
方法接收一个回调函数作为参数,这个回调函数不接受任何参数。这是因为它不关心Promise的最终状态,只关心操作是否已完成。
promise
.finally(() => {
// 清理代码
});
与then()
和catch()
不同,finally()
回调中:
- 不会接收任何参数
- 返回的Promise会保持原始Promise的状态
- 如果抛出异常,会返回一个被拒绝的Promise
finally() 与 then/catch 的区别
finally()
在行为上有几个关键区别:
- 状态传递:
finally()
会将原始Promise的状态传递给链中的下一个Promise,除非在finally()
回调中抛出错误。
Promise.resolve(42)
.finally(() => {})
.then(val => console.log(val)); // 输出42
Promise.reject('error')
.finally(() => {})
.catch(err => console.log(err)); // 输出'error'
- 返回值处理:在
finally()
中返回的值不会影响Promise链,除非返回的是一个Promise。
Promise.resolve('hello')
.finally(() => 'world')
.then(val => console.log(val)); // 输出'hello'
finally() 的常见使用场景
清理资源
let isLoading = true;
fetch('/api/data')
.then(handleResponse)
.catch(handleError)
.finally(() => {
isLoading = false;
console.log('加载状态结束');
});
统一日志记录
function apiCall() {
console.log('API调用开始');
return fetch('/api')
.finally(() => console.log('API调用结束'));
}
组合使用案例
let connection;
openDatabase()
.then(conn => {
connection = conn;
return connection.query('SELECT * FROM users');
})
.then(processResults)
.catch(logError)
.finally(() => {
if (connection) {
connection.close();
}
});
finally() 的错误处理
如果在finally()
回调中抛出错误,它会覆盖原始Promise的状态:
Promise.resolve('success')
.finally(() => {
throw new Error('error in finally');
})
.then(
val => console.log('不会执行', val),
err => console.log('捕获错误:', err) // 输出"捕获错误: Error: error in finally"
);
finally() 的Polyfill实现
在不支持finally()
的环境中,可以用以下方式模拟:
if (!Promise.prototype.finally) {
Promise.prototype.finally = function(callback) {
return this.then(
value => Promise.resolve(callback()).then(() => value),
reason => Promise.resolve(callback()).then(() => { throw reason; })
);
};
}
finally() 在async/await中的使用
finally()
也可以与async/await语法配合使用:
async function fetchData() {
try {
const response = await fetch('/api/data');
return await response.json();
} catch (error) {
console.error('请求失败:', error);
throw error;
} finally {
console.log('请求处理完成');
cleanup();
}
}
finally() 的性能考虑
虽然finally()
提供了便利,但在性能关键路径上需要注意:
- 它增加了微任务队列的长度
- 在紧密循环中使用可能会影响性能
- 简单的状态清理可以直接放在then/catch中
// 不推荐在循环中大量使用
for (let i = 0; i < 1000; i++) {
fetch(`/api/item/${i}`)
.finally(cleanup); // 会产生1000个微任务
}
finally() 的浏览器兼容性
finally()
在以下环境中得到支持:
- Chrome 63+
- Firefox 58+
- Safari 11.1+
- Edge 18+
- Node.js 10+
对于旧环境,需要使用前面提到的polyfill或Babel转译。
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:Promise与回调地狱的解决
下一篇:Promise的静态方法