阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > Promise.prototype.finally()

Promise.prototype.finally()

作者:陈川 阅读数:5522人阅读 分类: JavaScript

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()方法有几个重要的行为特点值得注意:

  1. 回调函数不接收任何参数,因为它不关心Promise的最终状态
  2. 返回一个新的Promise,其值与原Promise相同
  3. 如果回调函数抛出错误或返回一个rejected Promise,则会传递这个错误
  4. 在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()有几个关键区别:

  1. 参数处理then()catch()回调接收Promise的结果或错误,而finally()不接收任何参数
  2. 返回值影响finally()回调的返回值不会影响Promise链,除非抛出错误
  3. 执行时机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()在多种场景下非常有用:

  1. 资源清理:无论操作成功与否都需要执行的清理工作
  2. 加载状态管理:隐藏加载指示器
  3. 数据库连接:关闭数据库连接
  4. 动画控制:停止加载动画
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()时需要注意以下几点:

  1. 回调函数中的错误:如果在finally()回调中抛出错误,会覆盖之前的Promise状态
  2. 返回值finally()回调的返回值会被忽略,除非返回一个rejected Promise
  3. 异步清理:如果清理工作需要异步完成,应该返回一个Promise
// 错误示例:finally中的错误会覆盖原有结果
Promise.resolve('成功')
  .finally(() => { throw new Error('finally错误') })
  .catch(e => console.log(e.message)); // 输出: finally错误

// 正确处理异步清理
somePromise
  .finally(() => {
    return asyncCleanup(); // 返回Promise确保异步清理完成
  });

与其他异步特性的结合

finally()可以很好地与其他异步特性结合使用:

  1. async/await:在async函数中使用finally
  2. Promise.all:与Promise.all()等组合方法一起使用
  3. 可取消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()提供了便利,但在性能敏感的场景中需要考虑:

  1. 微任务队列finally()会增加微任务队列的长度
  2. 内存使用:长Promise链中的多个finally()会增加内存开销
  3. 错误处理:额外的finally()回调可能增加错误处理复杂度
// 避免不必要的finally链
// 不推荐
promise
  .finally(() => {})
  .finally(() => {})
  .finally(() => {});

// 推荐
promise.finally(() => {
  // 合并所有清理逻辑
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌