深入理解 JavaScript Promise:从构造函数到实战应用
在 JavaScript 异步编程领域,Promise 是解决回调地狱、规范异步流程的核心方案。它将异步操作的结果以 "状态" 形式封装,让异步逻辑更具可读性、可维护性,也是现代前端框架(Vue、React)和 Node.js 开发中不可或缺的基础。
一、Promise 核心概念:异步操作的 "状态容器"
在学习具体 API 之前,我们需要先明确 Promise 的本质 ------ 它是一个代表异步操作最终完成(或失败)及其结果值的对象。
1. 三个核心状态(不可逆)
Promise 的生命周期围绕三种状态展开,且状态一旦改变就无法修改:
-
pending:初始状态,异步操作尚未完成(等待中); -
fulfilled(别名 resolved):异步操作成功完成,状态从pending不可逆转为fulfilled; -
rejected:异步操作失败,状态从pending不可逆转为rejected。
2. 核心价值
-
解决 "回调地狱":避免多层嵌套的回调函数,用链式调用替代;
-
统一异步流程:无论成功 / 失败,都有标准化的处理方式;
-
状态可追踪:通过状态变化记录异步操作结果,便于调试和逻辑控制。
二、Promise 构造函数:创建异步操作的 "容器"
Promise 构造函数是所有 Promise 实例的 "源头",用于封装一个异步操作,并定义状态变化的逻辑。
1. 语法格式
new Promise((resolve, reject) => {
  // 异步操作逻辑(如网络请求、文件读取、定时器等)
  if (异步操作成功) {
  resolve(成功结果); // 状态:pending → fulfilled,传递成功数据
  } else {
  reject(失败原因); // 状态:pending → rejected,传递失败信息(推荐用Error对象)
  }
});
2. 关键参数解析
-
executor:必传的函数参数,Promise 实例创建时立即执行(同步执行); -
resolve:函数类型参数,调用后触发状态变更,将成功结果传递给后续处理函数; -
reject:函数类型参数,调用后触发状态变更,将失败原因传递给错误处理函数。
3. 基础实例:模拟网络请求
// 封装一个"模拟用户数据请求"的Promise
const fetchUser = (userId) => {
  return new Promise((resolve, reject) => {
  // 模拟异步操作(setTimeout替代网络请求延迟)
  setTimeout(() => {
  const mockUsers = { 1: "张三", 2: "李四", 3: "王五" };
  const userName = mockUsers\[userId];
   
  if (userName) {
  // 成功:传递用户信息(可是任意类型:字符串、对象、数组等)
  resolve({ code: 200, data: { userId, userName } });
  } else {
  // 失败:传递Error对象(包含错误信息和堆栈,便于调试)
  reject(new Error(\`用户ID=\${userId}不存在\`));
  }
  }, 1000); // 1秒后执行
  });
};
// 调用函数(此时返回Promise实例,状态为pending)
const promise = fetchUser(1);
console.log(promise); // Promise { \<pending> }
三、Promise 对象方法:处理异步结果的 "链式工具"
每个 Promise 实例都自带三个核心对象方法(then、catch、finally),用于处理异步操作的结果。它们的共同特点是返回新的 Promise 实例,因此支持链式调用,这也是解决回调地狱的关键。
1. then ():处理成功状态的核心方法
作用
当 Promise 状态变为fulfilled时,执行成功回调;也可可选配置失败回调(替代catch)。
语法
promise.then(
  (successResult) => { /\* 成功处理逻辑 \*/ }, // 必选:接收resolve传递的结果
  (errorReason) => { /\* 失败处理逻辑 \*/ } // 可选:接收reject传递的原因
);
关键特性
-
返回新的 Promise:新 Promise 的状态由回调函数的返回值决定;
-
若回调返回普通值(非 Promise),新 Promise 状态为
fulfilled,结果为该普通值; -
若回调返回 Promise,新 Promise 状态与返回的 Promise 状态一致;
-
若回调抛出错误,新 Promise 状态为
rejected。
-
示例:链式调用处理结果
fetchUser(1)
  .then((res) => {
  console.log("请求成功:", res.data.userName); // 输出:请求成功:张三
  return res.data.userId; // 返回普通值,传递给下一个then
  })
  .then((userId) => {
  console.log("上一步传递的用户ID:", userId); // 输出:上一步传递的用户ID:1
  return fetchUser(2); // 返回新的Promise,下一个then等待其状态变化
  })
  .then((res) => {
  console.log("第二个用户:", res.data.userName); // 输出:第二个用户:李四
  });
2. catch ():专门处理失败状态的方法
作用
捕获 Promise 的rejected状态(包括then回调中抛出的错误),是处理异步错误的 "标准方案"。
语法
promise.catch((errorReason) => {
  /\* 错误处理逻辑 \*/
});
关键特性
-
捕获范围:不仅捕获当前 Promise 的
reject,还捕获前面链式调用中所有then回调抛出的错误; -
返回新的 Promise:即使执行了
catch,后续仍可继续链式调用then。
示例:错误捕获
fetchUser(99) // 不存在的用户ID,会触发reject
  .then((res) => {
  console.log("请求成功:", res);
  })
  .catch((err) => {
  console.log("捕获到错误:", err.message); // 输出:捕获到错误:用户ID=99不存在
  // 可选:处理错误后,返回新值让后续then继续执行
  return "默认用户";
  })
  .then((userName) => {
  console.log("最终用户:", userName); // 输出:最终用户:默认用户(catch后仍可链式调用)
  });
3. finally ():无论成败都执行的方法
作用
Promise 状态变更后(无论fulfilled还是rejected),必然执行的回调,常用于清理资源(如关闭加载动画、释放连接)。
语法
promise.finally(() => {
  /\* 无论成功失败都要执行的逻辑 \*/
});
关键特性
-
无参数:回调函数不接收成功结果或失败原因;
-
不改变原状态:返回的新 Promise 状态与原 Promise 一致,仅附加 "收尾逻辑"。
示例:资源清理
console.log("开始请求(显示加载动画)");
fetchUser(3)
  .then((res) => {
  console.log("请求成功:", res.data.userName);
  })
  .catch((err) => {
  console.log("请求失败:", err.message);
  })
  .finally(() => {
  console.log("请求结束(关闭加载动画)"); // 无论成败都会执行
  });
四、Promise 类方法:静态工具,简化异步操作
Promise 类提供了 6 个核心静态方法(resolve、reject、all、allSettled、race、any),无需创建实例即可调用,主要用于快速创建 Promise 或批量处理多个 Promise。
1. Promise.resolve ():快速创建成功状态的 Promise
作用
直接返回一个状态为fulfilled的 Promise,省去手动编写构造函数的麻烦。
语法
Promise.resolve(value); // value:成功结果(可是普通值、Promise、thenable对象)
特殊规则
-
若
value是 Promise 实例:直接返回该实例,状态不变; -
若
value是 "thenable 对象"(含then方法的对象):会自动执行then方法,转化为 Promise 状态。
示例
// 1. 普通值
Promise.resolve("hello").then((res) => console.log(res)); // 输出:hello
// 2. Promise实例
const existingPromise = fetchUser(1);
Promise.resolve(existingPromise).then((res) => console.log(res.data.userName)); // 输出:张三
// 3. thenable对象
const thenable = {
  then(resolve) {
  setTimeout(() => resolve("thenable转化的结果"), 500);
  }
};
Promise.resolve(thenable).then((res) => console.log(res)); // 输出:thenable转化的结果
2. Promise.reject ():快速创建失败状态的 Promise
作用
直接返回一个状态为rejected的 Promise,用于快速模拟失败场景。
语法
Promise.reject(reason); // reason:失败原因(推荐用Error对象)
示例
Promise.reject(new Error("主动触发失败"))
  .catch((err) => console.log(err.message)); // 输出:主动触发失败
3. Promise.all ():"全成则成,一败则败" 的批量处理
作用
接收一个可迭代对象(通常是 Promise 数组),等待所有 Promise 都变为fulfilled后,返回成功结果数组;若有一个 Promise 变为rejected,立即返回该失败原因。
语法
Promise.all(promiseArray).then((results) => { /\* 所有成功的结果数组 \*/ }).catch((err) => { /\* 第一个失败的原因 \*/ });
关键特性
-
结果顺序:返回的结果数组顺序与输入的 Promise 数组顺序一致(即使某个 Promise 先完成);
-
失败快速返回:只要有一个失败,立即终止,不等待其他 Promise。
示例:批量请求多个用户
const promise1 = fetchUser(1);
const promise2 = fetchUser(2);
const promise3 = fetchUser(3);
Promise.all(\[promise1, promise2, promise3])
  .then((results) => {
  // results是三个Promise的成功结果数组,顺序与输入一致
  const userNames = results.map((res) => res.data.userName);
  console.log("所有用户:", userNames); // 输出:所有用户:\[ '张三', '李四', '王五' ]
  })
  .catch((err) => {
  console.log("请求失败:", err.message);
  });
4. Promise.allSettled ():"等待所有,无论成败" 的批量处理
作用
等待所有 Promise 都完成(无论fulfilled还是rejected),返回每个 Promise 的详细结果对象,适合需要知道所有异步操作最终状态的场景(如批量导入数据后的结果统计)。
语法
Promise.allSettled(promiseArray).then((results) => { /\* 所有操作的结果数组 \*/ });
结果对象结构
每个结果对象包含两个属性:
-
status:fulfilled(成功)或rejected(失败); -
value:status为fulfilled时存在,存储成功结果; -
reason:status为rejected时存在,存储失败原因。
示例
const promise1 = fetchUser(1);
const promise2 = fetchUser(99); // 失败
const promise3 = fetchUser(3);
Promise.allSettled(\[promise1, promise2, promise3])
  .then((results) => {
  results.forEach((result, index) => {
  if (result.status === "fulfilled") {
  console.log(\`第\${index+1}个请求成功:\`, result.value.data.userName);
  } else {
  console.log(\`第\${index+1}个请求失败:\`, result.reason.message);
  }
  });
  /\* 输出:
  第1个请求成功:张三
  第2个请求失败:用户ID=99不存在
  第3个请求成功:王五
  \*/
  });
5. Promise.race ():"谁先结束,谁决定结果"
作用
接收一个可迭代对象,等待第一个完成的 Promise(无论成功或失败),返回该 Promise 的结果或原因。
语法
Promise.race(promiseArray).then((result) => { /\* 第一个成功的结果 \*/ }).catch((err) => { /\* 第一个失败的原因 \*/ });
适用场景
- 超时控制:比如 "网络请求 5 秒内未响应则判定为失败"。
示例:超时控制
// 模拟网络请求(可能超时)
const request = fetchUser(1);
// 模拟超时定时器(500毫秒后失败)
const timeout = new Promise((\_, reject) => {
  setTimeout(() => reject(new Error("请求超时")), 500);
});
// 谁先完成,就取谁的结果
Promise.race(\[request, timeout])
  .then((res) => console.log("成功:", res.data.userName))
  .catch((err) => console.log("失败:", err.message)); // 输出:失败:请求超时(因为request需要1秒,timeout先触发)
6. Promise.any ():"谁先成功,谁决定结果"
作用
接收一个可迭代对象,等待第一个成功 的 Promise,返回其结果;若所有 Promise 都失败,返回一个包含所有失败原因的AggregateError。
语法
Promise.any(promiseArray).then((result) => { /\* 第一个成功的结果 \*/ }).catch((err) => { /\* 所有失败的原因集合 \*/ });
与race的区别
-
race:第一个结束(无论成败)就返回; -
any:第一个成功才返回,所有失败才返回错误。
示例
const promise1 = fetchUser(99); // 失败
const promise2 = fetchUser(2); // 成功(1秒)
const promise3 = fetchUser(98); // 失败
Promise.any(\[promise1, promise2, promise3])
  .then((res) => {
  console.log("第一个成功的请求:", res.data.userName); // 输出:第一个成功的请求:李四
  })
  .catch((err) => {
  console.log("所有请求都失败:", err.errors); // 所有失败时触发
  });
五、Promise 实战综合案例:多接口并发请求与错误处理
结合前面的知识点,我们实现一个 "批量请求用户信息 + 超时控制 + 失败降级" 的实战场景:
// 1. 封装带超时的请求函数
const fetchWithTimeout = (userId, timeoutMs = 1500) => {
  const request = fetchUser(userId);
  const timeout = new Promise((\_, reject) => {
  setTimeout(() => reject(new Error(\`用户\${userId}请求超时\`)), timeoutMs);
  });
  return Promise.race(\[request, timeout]);
};
// 2. 批量请求多个用户(含不存在的用户)
const userIds = \[1, 2, 99, 3];
const requestList = userIds.map((id) => 
  fetchWithTimeout(id)
  .catch((err) => {
  // 单个请求失败降级:返回默认值,不影响其他请求
  console.log(\`用户\${id}请求失败:\`, err.message);
  return { code: 400, data: { userId: id, userName: "未知用户" } };
  })
);
// 3. 等待所有请求完成,处理最终结果
Promise.allSettled(requestList)
  .then((results) => {
  const validUsers = results
  .filter((result) => result.status === "fulfilled")
  .map((result) => result.value.data);
  console.log("最终有效用户列表:", validUsers);
  /\* 输出:
  用户99请求失败:用户ID=99不存在
  最终有效用户列表:\[
  { userId: 1, userName: '张三' },
  { userId: 2, userName: '李四' },
  { userId: 99, userName: '未知用户' },
  { userId: 3, userName: '王五' }
  ]
  \*/
  });
六、核心要点总结
-
状态不可逆 :Promise 的
pending状态一旦转为fulfilled或rejected,就无法再次改变; -
链式调用本质 :
then、catch、finally均返回新 Promise,这是解决回调地狱的核心; -
类方法选型:
-
快速创建:
resolve(成功)、reject(失败); -
批量成功:
all(全成则成)、any(一成就成); -
批量结果:
allSettled(等待所有结果); -
竞争场景:
race(先结束者决定);
- 错误处理 :
catch能捕获前面所有链式操作的错误,建议每个 Promise 链末尾都添加catch。