🤝 JavaScript 异步基石:Promise 完全指南
🤔 为什么我们需要 Promise?
在 ES6 之前,处理异步操作(如网络请求、定时器)主要依赖回调函数 (Callback) 。当业务逻辑复杂时,多层嵌套的回调会导致代码难以阅读和维护,这就是著名的 "回调地狱" (Callback Hell)。
javascript
// ❌ 回调地狱示例
getData(function (a) {
getMoreData(a, function (b) {
getMoreData(b, function (c) {
getMoreData(c, function (d) {
console.log(d);
});
});
});
});
Promise 的出现解决了两个核心痛点:
- 代码扁平化 :通过
.then()链式调用,避免深层嵌套。 - 统一错误处理 :通过
.catch()集中捕获异常,不再需要在每个回调里写if (err)。
通俗比喻:
- 回调函数:像是你去餐厅点餐,服务员说:"菜好了我会喊你。"然后你就一直盯着厨房,不敢干别的事。如果还要点饮料,得等菜好了再叫另一个服务员。
- Promise :像是服务员给了你一个取餐器(Promise 对象)。你可以拿着取餐器去玩手机(执行其他代码)。当菜好了,取餐器会震动(状态变为 Resolved),你再去拿菜。如果厨房着火了,取餐器会报警(状态变为 Rejected)。
📂 目录
- [🔄 核心概念:三种状态](#🔄 核心概念:三种状态)
- [🛠️ 基本用法:创建与消费](#🛠️ 基本用法:创建与消费)
- [⛓️ 链式调用:.then 的秘密](#⛓️ 链式调用:.then 的秘密)
- [🚀 静态方法:all, race, allSettled](#🚀 静态方法:all, race, allSettled)
- [⚠️ 常见误区与最佳实践](#⚠️ 常见误区与最佳实践)
- [💡 总结](#💡 总结)
1. 🔄 核心概念:三种状态
Promise 是一个对象,代表一个异步操作的最终完成(或失败)及其结果值。它有三个互斥的状态:
| 状态 | 英文 | 说明 | 是否可逆 |
|---|---|---|---|
| 待定 | pending |
初始状态,操作正在进行中 | - |
| 已兑现 | fulfilled (resolved) |
操作成功完成,有一个结果值 | ✅ 不可逆 |
| 已拒绝 | rejected |
操作失败,有一个失败原因 | ✅ 不可逆 |
关键点 :
状态一旦从
pending变为fulfilled或rejected,就永远固定,不会再改变。这保证了结果的确定性。
2. 🛠️ 基本用法:创建与消费
✅ 创建 Promise
javascript
const myPromise = new Promise((resolve, reject) => {
// 模拟异步操作
setTimeout(() => {
const success = true;
if (success) {
resolve("操作成功!"); // 状态变为 fulfilled
} else {
reject(new Error("操作失败!")); // 状态变为 rejected
}
}, 1000);
});
✅ 消费 Promise
使用 .then() 处理成功,.catch() 处理失败。
javascript
myPromise
.then((result) => {
console.log(result); // "操作成功!"
})
.catch((error) => {
console.error(error.message); // "操作失败!"
});
3. ⛓️ 链式调用:.then 的秘密
.then() 方法本身也会返回一个新的 Promise,这使得我们可以链式调用。
javascript
fetchUser()
.then((user) => {
console.log("获取用户:", user);
return fetchOrders(user.id); // 返回一个新的 Promise
})
.then((orders) => {
console.log("获取订单:", orders);
return orders[0].id; // 返回普通值,会自动包裹成 resolved Promise
})
.then((orderId) => {
console.log("第一个订单ID:", orderId);
})
.catch((err) => {
console.error("任一环节出错:", err);
});
💡 核心规则
- 如果
.then()中返回的是一个 Promise ,下一个.then()会等待这个 Promise 结算。 - 如果返回的是 普通值 ,下一个
.then()会立即接收到这个值。 - 如果抛出 错误 ,流程会跳转到最近的
.catch()。
4. 🚀 静态方法:All, Race, AllSettled
当需要处理多个 Promise 时,ES6 提供了几种强大的组合工具。
✅ Promise.all([p1, p2, ...])
- 行为:并行执行所有 Promise。
- 成功:所有都成功时,返回包含所有结果的数组。
- 失败 :只要有一个失败,立即返回该失败原因(短路效应)。
- 场景:页面初始化需要同时加载用户信息和配置信息,缺一不可。
✅ Promise.race([p1, p2, ...])
-
行为 :并行执行,谁快用谁。
-
结果:返回第一个结算(无论成功或失败)的 Promise 的结果。
-
场景:设置请求超时。
javascriptconst timeout = new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), 5000), ); Promise.race([fetch("/api/data"), timeout]) .then((res) => console.log(res)) .catch((err) => console.error(err));
✅ Promise.allSettled([p1, p2, ...]) (ES2020)
- 行为 :并行执行,等待所有结束。
- 结果 :返回一个数组,每个元素包含
{ status: 'fulfilled', value: ... }或{ status: 'rejected', reason: ... }。 - 场景:批量上传文件,希望知道哪些成功了,哪些失败了,而不是因为一个失败就全部取消。
5. ⚠️ 常见误区与最佳实践
❌ 误区 1:在 .then() 中嵌套 Promise
javascript
// ❌ 糟糕写法
promise1.then((res1) => {
promise2.then((res2) => {
console.log(res1, res2);
});
});
// ✅ 推荐写法:利用链式返回
promise1
.then((res1) => {
return promise2;
})
.then((res2) => {
// 这里如果需要 res1,可以在外层作用域获取,或使用 Promise.all
console.log(res2);
});
❌ 误区 2:忘记返回 Promise
在链式调用中,如果忘记 return,下一个 .then() 接收到的将是 undefined。
javascript
// ❌ 错误
.then(res => {
fetchData(); // 没有 return
})
.then(nextRes => {
console.log(nextRes); // undefined
});
// ✅ 正确
.then(res => {
return fetchData();
})
❌ 误区 3:吞掉错误
javascript
// ❌ 危险
promise.catch((err) => {
console.log(err);
// 没有重新抛出或返回 rejected promise,链条会变成 resolved
});
// ✅ 建议
promise.catch((err) => {
console.error(err);
throw err; // 或者 return Promise.reject(err);
});
6. 💡 总结
| 特性 | 说明 |
|---|---|
| 状态 | Pending → Fulfilled / Rejected (不可逆) |
| 链式调用 | .then() 返回新 Promise,实现扁平化异步流 |
| 错误冒泡 | 任何环节的错误都会跳过中间的 .then,直达 .catch |
| 并发控制 | all (全成功), race (竞态), allSettled (全结算) |
🚀 博主寄语 :
Promise 是现代 JavaScript 异步编程的基石。
虽然
async/await语法糖让代码看起来更像同步,但理解 Promise 的底层的状态流转和链式机制,依然是写出健壮、高效异步代码的关键。记住口诀 :
新建 Promise 传执行器,
Resolve Reject 定结局。
Then 链调用返新包,
Catch 兜底捕异常。
All Race Settled 各不同,
异步编程心不慌。
希望这篇文档能帮你彻底搞懂 Promise!如果有疑问,欢迎在评论区留言。👇
喜欢这篇文章吗?记得点赞、收藏、转发哦! ❤️