【前端】ES6 引入的异步编程解决方案Promise 详解

发布于:2025-05-01 ⋅ 阅读:(36) ⋅ 点赞:(0)

Promise 详解

在这里插入图片描述


1. 基本概念
  • 定义:Promise 是 ES6 引入的异步编程解决方案,表示一个异步操作的最终完成(或失败)及其结果值
  • 核心作用:替代回调函数,解决“回调地狱”问题,提供更清晰的异步流程控制。

2. 核心状态

Promise 有三种状态:

状态 描述
pending 初始状态,既未成功也未失败。
fulfilled 操作成功完成,Promise 结果可通过 .then() 获取。
rejected 操作失败,错误信息可通过 .catch() 获取。

3. 核心方法
// 基本语法
const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve("成功"); // 改变为 fulfilled
    } else {
      reject("失败"); // 改变为 rejected
    }
  }, 1000);
});

// 使用 then/catch
promise
  .then((value) => console.log(value)) // 成功时执行
  .catch((error) => console.error(error)) // 失败时执行
  .finally(() => console.log("结束")); // 无论成功失败均执行

4. 核心特性
(1) 链式调用(Chaining)
promise
  .then((result) => {
    console.log(result); // 第一个 then
    return result * 2; // 返回值传递给下一个 then
  })
  .then((newResult) => console.log(newResult)); // 第二个 then
(2) 错误传递
  • 若某一层 .then() 抛出错误或调用 reject,后续 .then() 会跳过,直接进入最近的 .catch()
promise
  .then(() => {
    throw new Error("出错了"); // 触发错误
  })
  .then(() => console.log("不会执行"))
  .catch((err) => console.error(err)); // 捕获错误
(3) 微任务队列
  • Promise 的回调(.then/.catch)会被放入微任务队列,优先于宏任务(如 setTimeout)执行:
setTimeout(() => console.log("宏任务"), 0); // 后执行
Promise.resolve().then(() => console.log("微任务")); // 先执行

5. 静态方法
方法 作用
Promise.all() 等待所有 Promise 完成,返回成功结果数组,若有一个失败则立即返回错误。
Promise.race() 哪个 Promise 先完成,就返回其结果(成功或失败)。
Promise.resolve() 将现有值或 Promise 转换为 Promise 对象。
Promise.reject() 直接生成一个 rejected 状态的 Promise。
// 示例:Promise.all
Promise.all([p1, p2, p3])
  .then((results) => console.log(results)) // [result1, result2, result3]
  .catch((err) => console.error(err));

// 示例:Promise.race
Promise.race([slowPromise, fastPromise])
  .then((result) => console.log("最快完成的结果:", result));

6. 异常处理
  • 未捕获的 Rejection:若 Promise 抛出错误但未被 .catch() 捕获,会触发全局事件 unhandledrejection
    window.addEventListener('unhandledrejection', (event) => {
      console.error('未捕获的错误:', event.reason);
      event.preventDefault(); // 阻止默认的控制台报错
    });
    

7. async/await 语法糖
  • async/await 是基于 Promise 的语法糖,使异步代码更接近同步写法:
async function fetchData() {
  try {
    const response = await fetchAPI(); // 等待 Promise 完成
    console.log(response.data);
  } catch (error) {
    console.error(error);
  } finally {
    console.log("完成"); // 可选
  }
}

8. 常见陷阱
  1. 过早 resolve/reject
    如果在 Promise 构造函数中直接调用 resolve/reject,会立即执行,而非等待异步操作:

    // 错误示例:resolve 立即执行
    new Promise((resolve) => {
      resolve("提前完成"); // 这里会立即 resolve
      setTimeout(() => console.log("延迟操作"), 1000);
    });
    
  2. 遗忘 error 处理
    未在链式调用中添加 .catch() 可能导致错误未被捕获。

  3. this 指向问题
    .then() 中使用箭头函数可避免 this 丢失:

    const obj = {
      asyncMethod() {
        return new Promise(resolve => resolve("数据"));
      },
      process() {
        this.asyncMethod()
          .then(data => console.log(this)) // `this` 可能为 undefined
      }
    };
    // 解决方案:绑定 this 或使用箭头函数
    

9. 最佳实践
  1. 避免嵌套回调:使用链式调用或 async/await
  2. 统一错误处理:在顶层添加 .catch() 或全局监听 unhandledrejection
  3. 合理拆分 Promise:将复杂逻辑拆分为多个小 Promise,便于调试。
  4. 结合 async/await 提升可读性
    async function example() {
      const data = await fetchAPI(); // 等待完成再继续
      processData(data);
    }
    

10. 与 Callback 的对比
特性 Promise Callback
可读性 链式调用更清晰 嵌套回调易形成“回调地狱”
错误处理 集中通过 .catch() 处理 需在每个回调中单独处理错误
控制流 支持 Promise.all 等批量操作 需手动管理多个回调的完成状态
兼容性 需 polyfill 旧版浏览器 全局可用,无需额外支持

总结

Promise 是现代 JavaScript 异步编程的核心工具,通过清晰的状态管理和链式调用,极大提升了代码的可维护性。结合 async/await,可以进一步简化异步逻辑,避免回调地狱,是处理异步操作的首选方案。


网站公告

今日签到

点亮在社区的每一天
去签到