一、Promise是什么?外卖订单的终极哲学!
想象一下:你点外卖时,老板不会立马把饭塞你手里,而是给你一张小票(Promise)。这小票有三种命运:备餐中(pending)、完成(fulfilled) 或 退款(rejected)。而ECMAScript 6的Promise,就是JavaScript世界的“小票管理器”!
为什么Vue需要Promise?
比如在Vue组件中异步获取用户数据:
// 传统回调地狱(看完想砸键盘)
getUserInfo(userId, (user) => {
getOrders(user.id, (orders) => {
getLatestOrder(orders[0].id, (order) => {
this.orderDetail = order // 嵌套到怀疑人生
}, errorCallback)
}, errorCallback)
}, errorCallback)
换成Promise后:
getUserInfo(userId)
.then(user => getOrders(user.id))
.then(orders => getLatestOrder(orders[0].id))
.then(order => { this.orderDetail = order })
.catch(err => console.log('出错啦:', err))
瞬间从“金字塔代码”变成清爽的流水线!这就是Promise的链式调用魔法。
二、手撕Promise源码:自己造一个“小票生成器”
Promise的实现核心是状态机 + 回调队列。我们来手写一个迷你版MyPromise:
class MyPromise {
constructor(executor) {
this.state = 'pending' // 状态:pending, fulfilled, rejected
this.value = undefined // 成功值
this.reason = undefined // 失败原因
this.onFulfilledCallbacks = [] // 成功回调队列
this.onRejectedCallbacks = [] // 失败回调队列
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
this.onFulfilledCallbacks.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) {
// then方法必须返回新Promise,实现链式调用
return new MyPromise((resolve, reject) => {
const handleFulfilled = () => {
try {
const result = onFulfilled(this.value)
result instanceof MyPromise ? result.then(resolve, reject) : resolve(result)
} catch (err) {
reject(err)
}
}
const handleRejected = () => {
try {
const result = onRejected(this.reason)
result instanceof MyPromise ? result.then(resolve, reject) : resolve(result)
} catch (err) {
reject(err)
}
}
if (this.state === 'fulfilled') {
setTimeout(handleFulfilled, 0) // 模拟微任务队列
} else if (this.state === 'rejected') {
setTimeout(handleRejected, 0)
} else {
// pending状态时注册回调
this.onFulfilledCallbacks.push(() => setTimeout(handleFulfilled, 0))
this.onRejectedCallbacks.push(() => setTimeout(handleRejected, 0))
}
})
}
}
关键点解析:
- 状态不可逆:就像外卖订单不能从“已送达”变回“配送中”
- 异步执行:用setTimeout模拟微任务,避免阻塞主线程
- 链式调用奥秘:then返回新Promise,使
.then().then()成为可能
三、Vue + Promise实战:从入门到骚操作
场景1:异步组件加载(Vue Router官方推荐)
// 用Promise实现懒加载,减少首屏压力
const UserDetail = () => import('./UserDetail.vue') // 底层基于Promise
// 手动封装加载状态
const AsyncComponent = () => ({
component: import('./HeavyComponent.vue'),
loading: LoadingSpinner, // 加载中显示组件
error: ErrorComponent, // 出错时显示
delay: 200, // 延迟显示loading
timeout: 3000 // 超时时间
})
场景2:接口顺序请求(用户信息→订单列表→商品详情)
export default {
methods: {
async fetchData() {
try {
// 1. 并行请求优化(互不依赖的接口)
const [user, config] = await Promise.all([
this.$api.getUser(),
this.$api.getConfig()
])
// 2. 顺序请求(依赖前一个结果)
const orders = await this.$api.getOrders(user.id)
const latestOrder = await this.$api.getOrderDetail(orders[0].id)
this.data = { user, orders, latestOrder }
} catch (err) {
this.$toast.error('数据加载失败')
}
}
}
}
场景3:错误处理黑科技——自动重试机制
// 封装带重试功能的请求
function retryRequest(fn, times = 3) {
return new Promise(async (resolve, reject) => {
let lastError
for (let i = 0; i < times; i++) {
try {
const result = await fn()
return resolve(result)
} catch (err) {
lastError = err
console.log(`第${i+1}次尝试失败:`, err)
if (i < times - 1) await new Promise(r => setTimeout(r, 1000 * i)) // 延迟重试
}
}
reject(lastError)
})
}
// 在Vue中使用
export default {
methods: {
async fetchWithRetry() {
const data = await retryRequest(() => this.$api.getData(), 5)
this.data = data
}
}
}
四、Promise进阶技巧与避坑指南
1. 内存泄漏陷阱
// 错误示范:未取消的Promise可能导致内存泄漏
created() {
this.timer = setInterval(async () => {
this.data = await this.$api.getData() // 组件销毁后请求仍在继续
}, 5000)
}
// 正确做法:使用取消令牌
beforeDestroy() {
if (this.cancelToken) this.cancelToken.cancel('组件销毁')
}
2. 并行限制妙用(比如限制同时上传5个文件)
class PromisePool {
constructor(maxConcurrent) {
this.max = maxConcurrent
this.queue = []
this.running = 0
}
add(task) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject })
this.run()
})
}
run() {
while (this.running < this.max && this.queue.length) {
const { task, resolve, reject } = this.queue.shift()
this.running++
task().then(resolve, reject).finally(() => {
this.running--
this.run()
})
}
}
}
// Vue中使用
const pool = new PromisePool(3)
files.forEach(file => {
pool.add(() => this.uploadFile(file))
})
五、总结
Promise不仅是语法糖,更是异步编程范式的革命。在Vue项目中,结合async/await使用Promise,能让复杂异步流变得像同步代码一样直观。记住核心原则:
- 状态不可逆:就像人生没有后悔药
- 链式调用:像流水线一样处理任务
- 错误冒泡:一个catch抓住整个链条的错误
彩蛋:Promise.allSettled() 即使有失败也会返回所有结果,特别适合“部分成功也算成功”的场景哦!

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



