创见博客
Promise详解
七崽爱吃小饼干2026/01/29阅读 2专栏 JavaScript

一、先搞懂:Promise 是什么?

首先,Promise 是 JavaScript 中用于处理异步操作的一个内置对象(构造函数),它的核心作用是解决回调地狱问题,让异步代码的写法更优雅、更易维护。

可以用一个生活中的比喻来理解: 你去奶茶店点了一杯奶茶,店员给你一张取餐小票(Promise 对象),这张小票上包含了你的订单状态:

  1. 等待中(pending):奶茶还在制作,你不能马上拿到。
  2. 成功(fulfilled/resolved):奶茶制作完成,你可以取到奶茶(拿到异步操作的结果)。
  3. 失败(rejected):原料不足,奶茶做不了了(拿到异步操作的错误信息)。

这三种状态就是 Promise 的三种核心状态,且有一个重要特性:状态一旦改变(pending → fulfilled 或 pending → rejected),就再也无法修改(不可逆),就像奶茶店小票一旦显示“制作完成”,就不可能再变回“制作中”。

另外,Promise 还有一个值(value/reason):

  • 状态变为 fulfilled 时,会有一个成功的值(比如异步请求返回的数据)。
  • 状态变为 rejected 时,会有一个失败的原因(比如错误信息)。

二、为什么需要 Promise?

在 Promise 出现之前,JavaScript 处理异步操作(如 AJAX 请求、定时器)只能用回调函数,当遇到多个嵌套的异步操作时,就会出现回调地狱(Callback Hell)。

看一个回调地狱的例子(多层定时器嵌套):

javascript
// 需求:1秒后输出A,再过2秒输出B,再过3秒输出C
setTimeout(() => {
  console.log("A");
  setTimeout(() => {
    console.log("B");
    setTimeout(() => {
      console.log("C");
    }, 3000);
  }, 2000);
}, 1000);

这段代码的问题很明显:

  1. 代码层层嵌套,像“金字塔”一样,可读性极差。
  2. 后期维护困难,若要修改中间步骤,需要逐层拆解。
  3. 错误处理繁琐,每层回调都需要单独处理错误。

而 Promise 就是为了解决这些问题而生的,它可以将嵌套的回调改为链式调用,让代码更扁平、更易管理。

三、Promise 的核心用法

1. 基本语法:创建 Promise 实例

Promise 是一个构造函数,创建实例时需要传入一个执行器函数(executor),这个执行器函数会立即执行,且接收两个内置参数:resolve(成功回调)和 reject(失败回调)。

javascript
// 创建 Promise 实例
const myPromise = new Promise((resolve, reject) => {
  // 这里写异步操作逻辑(也可以写同步逻辑,只是Promise通常用于异步)
  const isSuccess = true; // 模拟异步操作是否成功

  setTimeout(() => {
    if (isSuccess) {
      // 异步操作成功:调用 resolve,将 Promise 状态改为 fulfilled
      // 可以传递一个成功的值(任意类型)
      resolve("异步操作执行成功!");
    } else {
      // 异步操作失败:调用 reject,将 Promise 状态改为 rejected
      // 可以传递一个失败的原因(任意类型,通常是 Error 对象)
      reject(new Error("异步操作执行失败!"));
    }
  }, 1000);
});

注意点:

  • 执行器函数会立即执行,不需要等待后续调用。
  • resolve 和 reject 只能调用其中一个(因为状态不可逆,调用第二个会无效)。
  • 建议 reject 传递 Error 对象,便于后续捕获错误详情。

2. 消费 Promise 实例:获取异步结果

创建 Promise 实例后,需要通过实例方法来消费它,获取成功或失败的结果,核心方法有两个:.then() 和 .catch()。

(1).then() 方法:处理成功状态

.then() 接收两个可选参数(都是函数):

  • 第一个参数:Promise 状态为 fulfilled 时的回调(处理成功结果)。
  • 第二个参数:Promise 状态为 rejected 时的回调(处理失败结果,可选,通常推荐用 .catch() 单独处理失败)。
javascript
// 消费 Promise 实例
myPromise.then(
  // 第一个参数:处理成功结果
  (successValue) => {
    console.log("成功:", successValue); // 输出:成功: 异步操作执行成功!
  },
  // 第二个参数:处理失败结果(可选)
  (errorReason) => {
    console.log("失败:", errorReason);
  }
);
(2).catch() 方法:处理失败状态

.catch() 专门用于处理 Promise 的 rejected 状态,以及 .then() 回调中抛出的错误,相当于 .then(null, onRejected) 的语法糖,更易读、更常用。

javascript
// 推荐写法:.then() 处理成功,.catch() 处理失败
myPromise
  .then((successValue) => {
    console.log("成功:", successValue);
  })
  .catch((errorReason) => {
    console.log("失败:", errorReason.message);
  });

修改 isSuccess = false,运行结果:失败: 异步操作执行失败!

(3).finally() 方法:无论成功失败都会执行

.finally() 是一个可选的实例方法,它的回调函数不接收任何参数,且无论 Promise 状态是 fulfilled 还是 rejected,都会在 .then() 或 .catch() 执行完毕后执行,通常用于做清理工作(如关闭加载动画、释放资源)。

javascript
myPromise
  .then((successValue) => {
    console.log("成功:", successValue);
  })
  .catch((errorReason) => {
    console.log("失败:", errorReason.message);
  })
  .finally(() => {
    console.log("异步操作结束,无论成功失败都会执行");
  });

3. 解决回调地狱:Promise 链式调用

.then() 方法有一个重要特性:返回值是一个新的 Promise 实例,这就意味着我们可以进行链式调用,将多层嵌套的异步操作改为扁平的链式写法,彻底解决回调地狱。

需求:用 Promise 实现“1秒后输出A,再过2秒输出B,再过3秒输出C”,避免回调地狱:

javascript
// 封装一个返回 Promise 的定时器函数
function delayLog(content, delayTime) {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(content);
      resolve(); // 执行完当前异步操作,调用 resolve 触发下一个 .then()
    }, delayTime);
  });
}

// 链式调用:扁平结构,无嵌套
delayLog("A", 1000)
  .then(() => delayLog("B", 2000))
  .then(() => delayLog("C", 3000));

这段代码的执行流程:

  1. 执行 delayLog("A", 1000),返回一个 Promise 实例。
  2. 1秒后输出 A,调用 resolve(),触发第一个 .then()。
  3. 第一个 .then() 中返回 delayLog("B", 2000)(新的 Promise 实例)。
  4. 再过2秒输出 B,调用 resolve(),触发第二个 .then()。
  5. 第二个 .then() 中返回 delayLog("C", 3000)(新的 Promise 实例)。
  6. 再过3秒输出 C,执行完毕。

可以看到,链式调用让异步代码的执行流程更清晰,就像同步代码一样从上到下执行,极大提升了可读性和可维护性。

四、补充:Promise 的常见误区

  1. 忘记返回 Promise:链式调用时,如果 .then() 中没有返回 Promise,下一个 .then() 会立即执行,无法实现异步操作的顺序执行。
  2. 错误捕获不全面:只捕获 Promise 本身的失败,忽略了 .then() 回调中抛出的同步错误(.catch() 可以捕获这类错误,推荐优先使用 .catch())。
  3. 认为 Promise 可以完全替代回调函数:Promise 本质上还是对回调函数的封装,只是让回调更优雅,并非完全抛弃回调。

六、总结

  1. Promise 是 JS 用于处理异步操作的内置对象,核心解决回调地狱问题,状态不可逆(pending → fulfilled / rejected)。
  2. 核心用法:new Promise() 创建实例(传入执行器函数),.then() 处理成功、.catch() 处理失败、.finally() 做收尾工作,支持链式调用实现异步操作顺序执行。
  3. 常用静态方法:Promise.resolve()/Promise.reject() 快速创建 Promise,Promise.all() 等待所有异步操作成功,Promise.race() 取最快完成的异步操作结果。
  4. Promise 让异步代码更扁平、更易维护,是后续 async/await(异步编程终极方案)的基础,掌握 Promise 是学好 JS 异步编程的关键。
评论
0/100