在 JavaScript 中,异步编程是处理网络请求、文件读写、定时器等耗时操作的核心方式。Promise 作为 ES6 引入的异步编程标准方案,解决了传统回调函数嵌套(“回调地狱”)的问题,提供了更优雅、可维护的异步流程控制方式。
一、为什么需要 Promise?
在 Promise 出现之前,异步操作依赖回调函数实现,但存在明显缺陷:
1. 回调地狱(Callback Hell)
多个异步操作串行执行时,回调函数会层层嵌套,代码可读性、可维护性极差:
// 传统回调:获取用户信息 → 获取用户订单 → 获取订单详情
getUser(userId, function(user) {
getUserOrders(user.id, function(orders) {
getOrderDetail(orders[0].id, function(detail) {
console.log("订单详情:", detail);
}, function(err) { console.log("订单详情失败:", err); });
}, function(err) { console.log("获取订单失败:", err); });
}, function(err) { console.log("获取用户失败:", err); });
- 嵌套层级越深,代码越难理解(“金字塔” 结构);
- 错误处理分散,每个回调都要单独处理异常;
- 难以实现并行、取消等复杂异步逻辑。
2. Promise 的核心价值
- 扁平化代码:用链式调用替代嵌套,代码线性执行,可读性提升;
- 集中错误处理:统一捕获异步流程中的所有错误;
- 标准化异步接口:定义了统一的异步操作规范,适配
async/await(ES2017 语法糖); - 支持复杂流程:原生支持并行(
Promise.all)、竞速(Promise.race)等场景。
二、Promise 核心概念
1. 定义
Promise 是一个对象,代表一个异步操作的最终完成(或失败) 及其结果值。
可以把 Promise 理解为一个 “异步操作的容器”,容器内的操作可能正在进行、已经成功、已经失败,但状态一旦确定就无法修改。
2. 三种状态(不可逆)
Promise 的生命周期有三种状态,状态变更不可逆:
| 状态 | 含义 | 状态变更触发条件 |
|---|---|---|
pending | 初始状态(等待中),异步操作未完成 | Promise 实例创建时默认状态 |
fulfilled(resolved) | 成功状态,异步操作完成且返回结果 | 调用 resolve() 时 |
rejected | 失败状态,异步操作出错或被拒绝 | 调用 reject() 时,或执行出错 |
注意:状态只能从
pending→fulfilled,或pending→rejected,一旦变更无法回退。
3. 核心方法
Promise 实例的核心方法是 then() 和 catch(),用于处理异步结果;此外还有静态方法(如 Promise.all)用于控制多个异步操作。
三、Promise 基本使用
1. 创建 Promise 实例
通过 new Promise((resolve, reject) => { ... }) 创建,传入一个执行器函数(立即执行):
- 执行器函数接收两个参数:
resolve(成功回调)和reject(失败回调); - 异步操作成功时调用
resolve(result),将状态改为fulfilled,并传递结果; - 异步操作失败时调用
reject(error),将状态改为rejected,并传递错误信息。
示例:封装一个异步定时器(模拟网络请求):
// 封装异步操作:2秒后返回成功结果
const delay = (ms, data) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
// 模拟成功场景
resolve(data); // 状态变为 fulfilled,传递结果 data
// 模拟失败场景(取消注释测试)
// reject(new Error(`超时${ms}ms失败`)); // 状态变为 rejected,传递错误
}, ms);
});
};
2. 处理结果:then() 和 catch()
then(onFulfilled, onRejected):处理成功 / 失败结果,返回一个新的 Promise(支持链式调用);- 第一个参数:状态为
fulfilled时执行,接收resolve传递的结果; - 第二个参数(可选):状态为
rejected时执行,接收reject传递的错误;
- 第一个参数:状态为
catch(onRejected):专门处理失败结果,等价于then(null, onRejected),更简洁。
基础用法
// 调用异步函数
delay(2000, "异步操作成功!")
.then((result) => {
console.log("成功:", result); // 2秒后输出:成功:异步操作成功!
})
.catch((error) => {
console.log("失败:", error.message); // 若调用 reject,输出错误信息
});
链式调用(解决回调地狱)
将之前的 “获取用户→订单→详情” 用 Promise 改写:
// 假设三个异步函数已封装为 Promise 版本
getUser(userId)
.then((user) => getUserOrders(user.id)) // 第一个 then 的返回值作为下一个 then 的参数
.then((orders) => getOrderDetail(orders[0].id))
.then((detail) => console.log("订单详情:", detail))
.catch((err) => console.log("流程失败:", err)); // 统一捕获所有环节的错误
- 链式调用中,每个
then可以返回新的 Promise 或普通值,下一个then会接收其结果; - 任何环节出错,都会直接进入
catch,无需单独处理每个错误。
3. 最终执行:finally()
finally(onFinally):无论 Promise 状态是成功还是失败,都会执行(ES2018 引入),常用于清理资源(如关闭加载动画、释放连接)。
delay(2000, "数据")
.then((res) => console.log("成功:", res))
.catch((err) => console.log("失败:", err))
.finally(() => console.log("异步操作结束,清理资源")); // 必执行
四、Promise 静态方法(处理多个异步操作)
Promise 提供了多个静态方法,用于高效控制多个异步操作的流程:
1. Promise.all(iterable):并行执行,全部成功才返回
- 接收一个可迭代对象(如数组),包含多个 Promise 实例;
- 所有 Promise 都变为
fulfilled时,返回一个新 Promise,结果为所有 Promise 结果的数组(顺序与传入顺序一致); - 只要有一个 Promise 变为
rejected,立即返回失败,结果为第一个失败的错误。
示例:并行请求多个接口,全部成功后处理数据:
const promise1 = delay(1000, "接口1数据");
const promise2 = delay(2000, "接口2数据");
const promise3 = delay(1500, "接口3数据");
Promise.all([promise1, promise2, promise3])
.then((results) => {
console.log("所有接口成功:", results); // 2秒后输出:["接口1数据", "接口2数据", "接口3数据"]
})
.catch((err) => {
console.log("某个接口失败:", err); // 若任意一个 reject,立即执行
});
2. Promise.race(iterable):竞速执行,先完成的为准
- 接收多个 Promise 实例,返回第一个完成(成功或失败)的 Promise 的结果;
- 无论第一个完成的是
fulfilled还是rejected,都会直接返回其状态和结果。
示例:设置请求超时时间(若接口 5 秒未响应,视为失败):
const request = delay(6000, "接口数据"); // 模拟接口耗时6秒
const timeout = delay(5000).then(() => {
throw new Error("请求超时"); // 5秒后触发失败
});
Promise.race([request, timeout])
.then((res) => console.log("请求成功:", res))
.catch((err) => console.log("请求失败:", err.message)); // 输出:请求超时
3. Promise.allSettled(iterable):并行执行,等待所有结果
- 无论每个 Promise 成功或失败,都会等待所有 Promise 完成;
- 返回的 Promise 始终是
fulfilled,结果为一个数组,每个元素包含对应 Promise 的状态(status)和结果(value或reason)。
示例:获取所有请求的结果(成功 / 失败都保留):
const promise1 = delay(1000, "成功1");
const promise2 = delay(1500).then(() => reject(new Error("失败2")));
const promise3 = delay(2000, "成功3");
Promise.allSettled([promise1, promise2, promise3])
.then((results) => {
results.forEach((result, index) => {
if (result.status === "fulfilled") {
console.log(`第${index+1}个成功:`, result.value);
} else {
console.log(`第${index+1}个失败:`, result.reason.message);
}
});
});
输出:
第1个成功: 成功1
第2个失败: 失败2
第3个成功: 成功3
4. Promise.resolve(value) 和 Promise.reject(reason):快速创建 Promise
Promise.resolve(value):快速创建一个fulfilled状态的 Promise,结果为value;Promise.reject(reason):快速创建一个rejected状态的 Promise,结果为reason。
示例:
// 快速创建成功的 Promise
Promise.resolve("直接成功").then((res) => console.log(res)); // 输出:直接成功
// 快速创建失败的 Promise
Promise.reject(new Error("直接失败")).catch((err) => console.log(err.message)); // 输出:直接失败
五、Promise 关键注意事项
1. 状态不可逆
一旦 Promise 从 pending 变为 fulfilled 或 rejected,后续再调用 resolve 或 reject 都无效:
const p = new Promise((resolve, reject) => {
resolve("成功");
reject("失败"); // 无效,状态已变为 fulfilled
});
p.then((res) => console.log(res)) // 输出:成功
.catch((err) => console.log(err)); // 不执行
2. then() 链式调用的返回值
每个 then() 都会返回一个新的 Promise,其状态由 then 内部的逻辑决定:
- 若
then中返回普通值(非 Promise),新 Promise 为fulfilled,结果为该值; - 若
then中返回 Promise,新 Promise 的状态和结果与该 Promise 一致; - 若
then中抛出错误,新 Promise 为rejected,结果为错误信息。
示例:
Promise.resolve(1)
.then((res) => res + 1) // 返回普通值 2 → 新 Promise 成功,结果 2
.then((res) => Promise.resolve(res * 2)) // 返回 Promise → 成功,结果 4
.then((res) => { throw new Error(`结果:${res}`); }) // 抛出错误 → 新 Promise 失败
.catch((err) => console.log(err.message)); // 输出:结果:4
3. 错误捕获
catch会捕获其之前所有then中的错误(包括异步操作的reject和同步代码的异常);- 若链式调用中没有
catch,未捕获的错误会触发全局unhandledrejection事件(浏览器 / Node.js 中),可能导致程序崩溃。
4. 避免 Promise 嵌套
虽然 Promise 解决了回调地狱,但仍可能出现不必要的嵌套,应始终使用链式调用:
// 错误:不必要的嵌套
delay(1000, "a")
.then((res) => {
delay(1000, res + "b")
.then((res2) => console.log(res2)); // 嵌套,不推荐
});
// 正确:链式调用
delay(1000, "a")
.then((res) => delay(1000, res + "b")) // 返回新 Promise
.then((res2) => console.log(res2)); // 输出:ab
六、Promise 与 async/await(语法糖)
ES2017 引入的 async/await 是 Promise 的语法糖,让异步代码看起来更像同步代码,可读性更强。其本质仍是基于 Promise 实现的。
示例:用 async/await 改写链式调用:
// 异步函数(async 关键字声明)
async function getOrderInfo(userId) {
try {
const user = await getUser(userId); // 等待 Promise 成功,获取结果
const orders = await getUserOrders(user.id); // 串行等待
const detail = await getOrderDetail(orders[0].id);
console.log("订单详情:", detail);
return detail; // 异步函数返回的结果会被包装为 Promise
} catch (err) {
console.log("流程失败:", err); // 统一捕获所有错误(等价于 catch)
} finally {
console.log("操作结束,清理资源"); // 等价于 finally
}
}
// 调用异步函数(返回 Promise)
getOrderInfo(123);
注意:
await只能在async函数内部使用;async函数的返回值默认是Promise.resolve(返回值)。
七、总结
Promise 是 JavaScript 异步编程的标准方案,核心价值在于:
- 用链式调用替代回调嵌套,解决 “回调地狱”;
- 统一错误处理,简化异常捕获;
- 提供标准化接口,支持
async/await语法糖; - 通过静态方法(
all/race/allSettled)高效控制多异步流程。
在实际开发中,Promise 已成为异步操作的基础(如 Fetch API、Axios、Node.js 异步模块等均基于 Promise 设计),掌握 Promise 是前端 / Node.js 开发的核心技能之一。
ES6 Promise异步编程详解

400

被折叠的 条评论
为什么被折叠?



