---
title: 「Promise完全指南」从青铜到王者的异步征服之路|手撕源码+花式操作+防坑秘籍
date: 2023-08-20
tags: [JavaScript, 异步编程, 前端进阶]
cover: https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/45b3d7f3c9c94b1eb5d6d7f4a5a0d7d4~tplv-k3u1fbpfcp-watermark.image?
---
# 🔥 你以为懂Promise?这些骚操作绝对让你瞳孔地震!
「面试造火箭,工作拧螺丝?」今天我们要造的这颗Promise火箭,保证让你在异步编程的宇宙中自由穿梭!准备好迎接认知颠覆了吗?
## 一、青铜局:Promise的十万个为什么
### 1.1 人类迷惑行为大赏
```javascript
// 经典回调地狱
getUser(userId, function(user) {
getOrders(user.id, function(orders) {
getItems(orders[0].id, function(items) {
// 更多嵌套...
});
});
});
当异步操作遇上俄罗斯套娃式回调,恭喜你解锁「代码屎山」成就!Promise就是来拯救这个世界的超人!
1.2 Promise的三重人格
const promise = new Promise((resolve, reject) => {
// 同步执行的执行器函数
if (/* 成功 */) {
resolve(value)
} else {
reject(reason)
}
})
// 状态不可逆的奥秘
promise
.then(value => { /* 成功回调 */ })
.catch(error => { /* 失败回调 */ })
.finally(() => { /* 终极回调 */ })
三个神秘状态:pending(等待)、fulfilled(成功)、rejected(失败)。就像薛定谔的猫,一旦确定就永不改变!
二、钻石局:手撕Promise源码(高能预警)
2.1 极简版实现
class MyPromise {
constructor(executor) {
this.state = 'pending'
this.value = undefined
this.reason = undefined
this.onResolvedCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
this.onResolvedCallbacks.forEach(fn => fn())
}
}
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value)
} else if (this.state === 'rejected') {
onRejected(this.reason)
} else {
this.onResolvedCallbacks.push(() => onFulfilled(this.value))
this.onRejectedCallbacks.push(() => onRejected(this.reason))
}
}
}
⚠️ 注意:这只是一个教学用简化版,真实实现要考虑更多边界条件!
2.2 链式调用的魔法
then(onFulfilled, onRejected) {
// 返回新Promise实现链式调用
return new MyPromise((resolve, reject) => {
const handle = (fn, arg) => {
try {
const result = fn(arg)
// 处理返回值类型
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
// ...省略状态判断
})
}
链式调用的核心秘密:每次then都返回新的Promise对象!
三、星耀局:Promise全家桶的花式操作
3.1 四大天王对比实验
| 方法 | 特点 | 成功条件 | 返回值 |
|---|---|---|---|
| Promise.all | 全部成功或任意失败 | 所有promise成功 | 结果数组 |
| Promise.race | 速度竞赛 | 第一个状态改变 | 第一个结果 |
| Promise.any | 至少一个成功 | 任一promise成功 | 第一个成功结果 |
| allSettled | 无论成败全记录 | 所有promise完成 | 状态结果对象数组 |
3.2 超实用代码配方
// 超时控制
function withTimeout(promise, timeout) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
])
}
// 进度条监控
function trackProgress(promises) {
let completed = 0
promises.forEach(p => {
p.finally(() => {
console.log(`进度:${++completed}/${promises.length}`)
})
})
return Promise.all(promises)
}
四、王者局:深藏不露的防坑秘籍
4.1 死亡陷阱TOP3
- 回调地狱转移术(伪链式调用)
// 错误示范!
promise.then(result => {
promise.then(...) // 形成新的回调地狱
})
- 吞没异常的黑洞
promise.then(() => {
throw new Error('消失的异常') // 未被捕获!
}).catch(() => { /* 这里也捕获不到! */ })
- 同步异步的量子纠缠
console.log('开始')
Promise.resolve().then(() => console.log('微任务'))
setTimeout(() => console.log('宏任务'), 0)
console.log('结束')
// 输出顺序:开始 → 结束 → 微任务 → 宏任务
4.2 终极最佳实践
- 永远返回promise链,避免中断
- 使用async/await时别忘try/catch
- 合理使用Promise.all提升性能
- 避免在Promise中执行阻塞操作
五、神级扩展:通往async/await的虫洞
async function fetchData() {
try {
const user = await fetchUser()
const orders = await fetchOrders(user.id)
return processData(orders)
} catch (error) {
// 统一错误处理
showErrorToast(error)
} finally {
hideLoading()
}
}
async/await本质是Promise的语法糖,但要注意:
- await只能在async函数中使用
- 并行请求仍需配合Promise.all
- 错误处理要全面