JavaScript Promise,包括构造函数、对象方法和类方法

深入理解 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 实例都自带三个核心对象方法(thencatchfinally),用于处理异步操作的结果。它们的共同特点是返回新的 Promise 实例,因此支持链式调用,这也是解决回调地狱的关键。

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

作用

当 Promise 状态变为fulfilled时,执行成功回调;也可可选配置失败回调(替代catch)。

语法
复制代码
promise.then(

&#x20; (successResult) => { /\* 成功处理逻辑 \*/ }, // 必选:接收resolve传递的结果

&#x20; (errorReason) => { /\* 失败处理逻辑 \*/ }  // 可选:接收reject传递的原因

);
关键特性
  • 返回新的 Promise:新 Promise 的状态由回调函数的返回值决定;

    • 若回调返回普通值(非 Promise),新 Promise 状态为fulfilled,结果为该普通值;

    • 若回调返回 Promise,新 Promise 状态与返回的 Promise 状态一致;

    • 若回调抛出错误,新 Promise 状态为rejected

示例:链式调用处理结果
复制代码
fetchUser(1)

&#x20; .then((res) => {

&#x20;   console.log("请求成功:", res.data.userName); // 输出:请求成功:张三

&#x20;   return res.data.userId; // 返回普通值,传递给下一个then

&#x20; })

&#x20; .then((userId) => {

&#x20;   console.log("上一步传递的用户ID:", userId); // 输出:上一步传递的用户ID:1

&#x20;   return fetchUser(2); // 返回新的Promise,下一个then等待其状态变化

&#x20; })

&#x20; .then((res) => {

&#x20;   console.log("第二个用户:", res.data.userName); // 输出:第二个用户:李四

&#x20; });

2. catch ():专门处理失败状态的方法

作用

捕获 Promise 的rejected状态(包括then回调中抛出的错误),是处理异步错误的 "标准方案"。

语法
复制代码
promise.catch((errorReason) => {

&#x20; /\* 错误处理逻辑 \*/

});
关键特性
  • 捕获范围:不仅捕获当前 Promise 的reject,还捕获前面链式调用中所有then回调抛出的错误;

  • 返回新的 Promise:即使执行了catch,后续仍可继续链式调用then

示例:错误捕获
复制代码
fetchUser(99) // 不存在的用户ID,会触发reject

&#x20; .then((res) => {

&#x20;   console.log("请求成功:", res);

&#x20; })

&#x20; .catch((err) => {

&#x20;   console.log("捕获到错误:", err.message); // 输出:捕获到错误:用户ID=99不存在

&#x20;   // 可选:处理错误后,返回新值让后续then继续执行

&#x20;   return "默认用户";

&#x20; })

&#x20; .then((userName) => {

&#x20;   console.log("最终用户:", userName); // 输出:最终用户:默认用户(catch后仍可链式调用)

&#x20; });

3. finally ():无论成败都执行的方法

作用

Promise 状态变更后(无论fulfilled还是rejected),必然执行的回调,常用于清理资源(如关闭加载动画、释放连接)。

语法
复制代码
promise.finally(() => {

&#x20; /\* 无论成功失败都要执行的逻辑 \*/

});
关键特性
  • 无参数:回调函数不接收成功结果或失败原因;

  • 不改变原状态:返回的新 Promise 状态与原 Promise 一致,仅附加 "收尾逻辑"。

示例:资源清理
复制代码
console.log("开始请求(显示加载动画)");

fetchUser(3)

&#x20; .then((res) => {

&#x20;   console.log("请求成功:", res.data.userName);

&#x20; })

&#x20; .catch((err) => {

&#x20;   console.log("请求失败:", err.message);

&#x20; })

&#x20; .finally(() => {

&#x20;   console.log("请求结束(关闭加载动画)"); // 无论成败都会执行

&#x20; });

四、Promise 类方法:静态工具,简化异步操作

Promise 类提供了 6 个核心静态方法(resolverejectallallSettledraceany),无需创建实例即可调用,主要用于快速创建 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 = {

&#x20; then(resolve) {

&#x20;   setTimeout(() => resolve("thenable转化的结果"), 500);

&#x20; }

};

Promise.resolve(thenable).then((res) => console.log(res)); // 输出:thenable转化的结果

2. Promise.reject ():快速创建失败状态的 Promise

作用

直接返回一个状态为rejected的 Promise,用于快速模拟失败场景。

语法
复制代码
Promise.reject(reason); // reason:失败原因(推荐用Error对象)
示例
复制代码
Promise.reject(new Error("主动触发失败"))

&#x20; .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])

&#x20; .then((results) => {

&#x20;   // results是三个Promise的成功结果数组,顺序与输入一致

&#x20;   const userNames = results.map((res) => res.data.userName);

&#x20;   console.log("所有用户:", userNames); // 输出:所有用户:\[ '张三', '李四', '王五' ]

&#x20; })

&#x20; .catch((err) => {

&#x20;   console.log("请求失败:", err.message);

&#x20; });

4. Promise.allSettled ():"等待所有,无论成败" 的批量处理

作用

等待所有 Promise 都完成(无论fulfilled还是rejected),返回每个 Promise 的详细结果对象,适合需要知道所有异步操作最终状态的场景(如批量导入数据后的结果统计)。

语法
复制代码
Promise.allSettled(promiseArray).then((results) => { /\* 所有操作的结果数组 \*/ });
结果对象结构

每个结果对象包含两个属性:

  • statusfulfilled(成功)或rejected(失败);

  • valuestatusfulfilled时存在,存储成功结果;

  • reasonstatusrejected时存在,存储失败原因。

示例
复制代码
const promise1 = fetchUser(1);

const promise2 = fetchUser(99); // 失败

const promise3 = fetchUser(3);

Promise.allSettled(\[promise1, promise2, promise3])

&#x20; .then((results) => {

&#x20;   results.forEach((result, index) => {

&#x20;     if (result.status === "fulfilled") {

&#x20;       console.log(\`第\${index+1}个请求成功:\`, result.value.data.userName);

&#x20;     } else {

&#x20;       console.log(\`第\${index+1}个请求失败:\`, result.reason.message);

&#x20;     }

&#x20;   });

&#x20;   /\* 输出:

&#x20;   第1个请求成功:张三

&#x20;   第2个请求失败:用户ID=99不存在

&#x20;   第3个请求成功:王五

&#x20;   \*/

&#x20; });

5. Promise.race ():"谁先结束,谁决定结果"

作用

接收一个可迭代对象,等待第一个完成的 Promise(无论成功或失败),返回该 Promise 的结果或原因。

语法
复制代码
Promise.race(promiseArray).then((result) => { /\* 第一个成功的结果 \*/ }).catch((err) => { /\* 第一个失败的原因 \*/ });
适用场景
  • 超时控制:比如 "网络请求 5 秒内未响应则判定为失败"。
示例:超时控制
复制代码
// 模拟网络请求(可能超时)

const request = fetchUser(1);

// 模拟超时定时器(500毫秒后失败)

const timeout = new Promise((\_, reject) => {

&#x20; setTimeout(() => reject(new Error("请求超时")), 500);

});

// 谁先完成,就取谁的结果

Promise.race(\[request, timeout])

&#x20; .then((res) => console.log("成功:", res.data.userName))

&#x20; .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])

&#x20; .then((res) => {

&#x20;   console.log("第一个成功的请求:", res.data.userName); // 输出:第一个成功的请求:李四

&#x20; })

&#x20; .catch((err) => {

&#x20;   console.log("所有请求都失败:", err.errors); // 所有失败时触发

&#x20; });

五、Promise 实战综合案例:多接口并发请求与错误处理

结合前面的知识点,我们实现一个 "批量请求用户信息 + 超时控制 + 失败降级" 的实战场景:

复制代码
// 1. 封装带超时的请求函数

const fetchWithTimeout = (userId, timeoutMs = 1500) => {

&#x20; const request = fetchUser(userId);

&#x20; const timeout = new Promise((\_, reject) => {

&#x20;   setTimeout(() => reject(new Error(\`用户\${userId}请求超时\`)), timeoutMs);

&#x20; });

&#x20; return Promise.race(\[request, timeout]);

};

// 2. 批量请求多个用户(含不存在的用户)

const userIds = \[1, 2, 99, 3];

const requestList = userIds.map((id) =>&#x20;

&#x20; fetchWithTimeout(id)

&#x20;   .catch((err) => {

&#x20;     // 单个请求失败降级:返回默认值,不影响其他请求

&#x20;     console.log(\`用户\${id}请求失败:\`, err.message);

&#x20;     return { code: 400, data: { userId: id, userName: "未知用户" } };

&#x20;   })

);

// 3. 等待所有请求完成,处理最终结果

Promise.allSettled(requestList)

&#x20; .then((results) => {

&#x20;   const validUsers = results

&#x20;     .filter((result) => result.status === "fulfilled")

&#x20;     .map((result) => result.value.data);

&#x20;   console.log("最终有效用户列表:", validUsers);

&#x20;   /\* 输出:

&#x20;   用户99请求失败:用户ID=99不存在

&#x20;   最终有效用户列表:\[

&#x20;     { userId: 1, userName: '张三' },

&#x20;     { userId: 2, userName: '李四' },

&#x20;     { userId: 99, userName: '未知用户' },

&#x20;     { userId: 3, userName: '王五' }

&#x20;   ]

&#x20;   \*/

&#x20; });

六、核心要点总结

  1. 状态不可逆 :Promise 的pending状态一旦转为fulfilledrejected,就无法再次改变;

  2. 链式调用本质thencatchfinally均返回新 Promise,这是解决回调地狱的核心;

  3. 类方法选型

  • 快速创建:resolve(成功)、reject(失败);

  • 批量成功:all(全成则成)、any(一成就成);

  • 批量结果:allSettled(等待所有结果);

  • 竞争场景:race(先结束者决定);

  1. 错误处理catch能捕获前面所有链式操作的错误,建议每个 Promise 链末尾都添加catch
相关推荐
桜吹雪36 分钟前
LangChain.js/DeepAgents可观测性
javascript·人工智能
神仙别闹44 分钟前
基于C++实现(控制台)应用递推法完成经典型算法的应用
开发语言·c++·算法
灵魂学者1 小时前
Vue3.x —— 父子通信
前端·javascript·vue.js·github
kk哥88991 小时前
inout参数传递机制的底层原理是什么?
java·开发语言
listhi5202 小时前
基于改进SET的时频分析MATLAB实现
开发语言·算法·matlab
勇气要爆发2 小时前
问:ES5和ES6的区别
前端·ecmascript·es6
友友马2 小时前
『QT』事件处理机制详解 (一)
开发语言·qt
芳草萋萋鹦鹉洲哦2 小时前
【vue/js】文字超长悬停显示的几种方式
前端·javascript·vue.js
孤独斗士3 小时前
maven的pom文件总结
java·开发语言