Promise作为JavaScript异步编程的核心解决方案,彻底解决了回调地狱的问题。但很多开发者只停留在“会用”层面,对其底层原理、核心方法实现一知半解。本文将从Promise的核心原理出发,手把手实现一个完整的Promise类,并深入剖析any、finally、allSettled、resolve等核心方法的实现逻辑。
一、Promise核心原理剖析
1. Promise的核心特性
-
三种状态:
pending(等待)、fulfilled(成功)、rejected(失败),状态一旦变更不可逆; -
异步回调:成功/失败的回调函数会被存入对应队列,状态变更时批量执行;
-
then方法:1. 默认返回一个 promise 对象,它的状态跟随 then 前面的那个 promise 的状态一起变更2. then的回调中返回了一个promise 对象,那么then 中的promise的状态就会跟随自身回调返回的那个promise 对象的状态变更
2. 手写Promise基础骨架
先实现Promise的核心构造函数和then方法,这是所有扩展方法的基础:
class MyPromise {
constructor(executor) {
this.state = 'pending'
this.onFulfilledCallbacks = [] // 存储成功回调函数的队列
this.value = undefined // 存储成功状态下的返回值
this.onRejectedCallbacks = [] // 存储失败回调函数的队列
this.reason = undefined // 存储失败状态下的错误的值
// 定义resolve函数:将Promise状态转为成功(fulfilled)
const resolve = (value) => {
if (this.state === 'pending') { // 只有状态为pending时才能修改,保证状态不可逆
this.state = 'fulfilled'
this.value = value
// 批量执行所有存储的成功回调,传入成功值
this.onFulfilledCallbacks.forEach((callback) => callback(value))
}
}
// 定义reject函数:将Promise状态转为失败(rejected)
const reject = (reason) => {
if (this.state === 'pending') { // 只有状态为pending时才能修改,保证状态不可逆
this.state = 'rejected'
// 保存失败的错误的值
this.reason = reason
// 批量执行所有存储的失败回调,传入错误原因
this.onRejectedCallbacks.forEach(callback => callback(reason))
}
}
// 立即执行执行器函数,并传入resolve和reject方法
executor(resolve, reject)
}
// then方法:Promise的核心方法,用于注册成功/失败回调,支持链式调用
then(onFulfilled, onRejected) { // 会将 cb 存入onFulfilledCallbacks/onRejectedCallbacks
// 兼容非函数参数:若onFulfilled不是函数,赋值为空函数,避免报错
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => { }
// 兼容非函数参数:若onRejected不是函数,赋值为空函数,避免报错
onRejected = typeof onRejected === 'function' ? onRejected : () => { }
// then方法返回新的Promise,实现链式调用(核心:新Promise的状态由回调执行结果决定)
const newPromise = new MyPromise((resolve, reject) => {
// 情况1:当前Promise状态已为成功(fulfilled),直接执行成功回调
if (this.state === 'fulfilled') { // resolve 已经先行了(状态提前变为成功,例如写了同步代码)
setTimeout(() => { // 用setTimeout模拟微任务(原生Promise的then回调属于微任务)
const result = onFulfilled(this.value) // 执行成功回调,获取回调返回结果
if (result instanceof MyPromise) {
// 递归调用then,将新Promise的resolve/reject传入,实现状态透传
result.then((res) => resolve(res), (err) => reject(err))
} else {
// 若返回值不是Promise,直接将新Promise转为成功态,传入返回值
resolve(result)
}
})
}
// 情况2:当前Promise状态已为失败(rejected),直接执行失败回调
if (this.state === 'rejected') { // reject 已经先行了(状态提前变为失败)
setTimeout(() => { // 用setTimeout模拟微任务
// 执行失败回调,获取回调返回结果
const result = onRejected(this.reason)
// 若回调返回值是Promise实例,新Promise的状态跟随该实例
if (result instanceof MyPromise) {
result.then((res) => resolve(res), (err) => reject(err))
} else {
// 若返回值不是Promise,直接将新Promise转为成功态(即使原状态是失败,回调执行无异常则新状态为成功)
resolve(result)
}
})
}
// 情况3:当前Promise状态为等待(pending),将回调存入对应队列,等待状态变更后执行
if (this.state === 'pending') {
// 存入成功回调,状态变为fulfilled时执行
this.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
// 执行成功回调,获取返回结果
const result = onFulfilled(value)
// 处理回调返回值,决定新Promise的状态
if (result instanceof MyPromise) {
result.then((res) => resolve(res), (err) => reject(err))
} else {
resolve(result)
}
})
})
// 存入失败回调,状态变为rejected时执行
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
// 执行失败回调,获取返回结果
const result = onRejected(this.reason)
// 处理回调返回值,决定新Promise的状态
if (result instanceof MyPromise) {
result.then((res) => resolve(res), (err) => reject(err))
} else {
resolve(result)
}
})
})
}
})
// 返回新Promise,实现链式调用
return newPromise
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
}
二、Promise核心静态方法实现
1. Promise.resolve:快速创建成功Promise
原理:静态方法,接收一个值,返回状态为fulfilled的Promise实例。
应用场景:请求拦截/响应拦截中快速包裹成功数据,简化异步处理。
static resolve(val) {
return new MyPromise((resolve) => {
resolve(val)
})
}
2. Promise.any:“只要一个成功就赢”
原理:遍历Promise数组,只要有一个成功就resolve;全部失败则reject(包含所有失败原因)。
特点:优先返回最快成功的Promise,适合“多源请求取最快成功结果”场景。
static any(promises) {
return new MyPromise((resolve, reject) => {
const errors = [] // 存储所有失败原因
let count = 0 // 失败计数器
// 空数组特殊处理
if (promises.length === 0) {
reject(new AggregateError([], 'All promises were rejected'))
return
}
promises.forEach((promise, index) => {
// 兼容非Promise值(视为成功)
MyPromise.resolve(promise).then(
(res) => {
resolve(res) // 只要一个成功,立即resolve
},
(err) => {
errors[index] = err
count++
// 全部失败时reject
if (count === promises.length) {
reject(new AggregateError(errors, 'All promises were rejected'))
}
}
)
})
})
}
3. Promise.allSettled:“等待所有结果,无论成败”
原理:等待所有Promise状态变更(成功/失败),返回一个永远成功的Promise,结果数组包含每个Promise的状态和值/原因。
应用场景:批量请求(如表单提交+数据上报),需要知道所有请求的最终状态,不因为单个失败中断。
static allSettled(promises) {
const promiseArray = Array.from(promises) // 转数组,兼容类数组
const result = []
let count = 0
return new MyPromise((resolve) => {
if (promiseArray.length === 0) {
resolve(result)
return
}
promiseArray.forEach((promise, index) => {
MyPromise.resolve(promise).then(
(res) => {
result[index] = { status: 'fulfilled', value: res }
},
(err) => {
result[index] = { status: 'rejected', reason: err }
}
).finally(() => {
count++
// 所有Promise处理完成后resolve
if (count === promiseArray.length) {
resolve(result)
}
})
})
})
}
4. Promise.finally:“无论成败都执行”
原理:实例方法,接收回调函数,无论原Promise状态如何,回调都会执行;返回新Promise,状态跟随原Promise(回调报错则变为失败)。
应用场景:异步请求后关闭loading、清理定时器等收尾操作。
finally(callback) {
return this.then(
(value) => {
// 执行回调,兼容返回Promise的情况
return MyPromise.resolve(callback()).then(() => value)
},
(reason) => {
return MyPromise.resolve(callback()).then(() => { throw reason })
}
)
}
5. 扩展:Promise.race和Promise.all
Promise.race:“速度竞赛,第一个变更状态的赢”
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach((promise) => {
MyPromise.resolve(promise).then(resolve, reject)
})
})
}
Promise.all:“全部成功才赢,一个失败就输”
static all(promises) {
return new MyPromise((resolve, reject) => {
const result = []
let count = 0
if (promises.length === 0) {
resolve(result)
return
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
(res) => {
result[index] = res
count++
if (count === promises.length) {
resolve(result)
}
},
(err) => {
reject(err) // 一个失败立即reject
}
)
})
})
}
三、核心知识点总结
-
状态不可逆:Promise一旦从
pending变为fulfilled/rejected,状态永久固定; -
then的链式调用:
then返回新Promise,状态跟随原Promise或回调返回的Promise; -
微任务模拟:原生Promise的
then回调属于微任务,本文用setTimeout模拟(宏任务),核心逻辑一致; -
方法选型技巧:
-
取最快成功结果 →
Promise.any; -
取最快变更结果(无论成败)→
Promise.race; -
全部成功才继续 →
Promise.all; -
需知道所有结果(无论成败)→
Promise.allSettled; -
收尾操作 →
finally; -
快速创建成功Promise →
Promise.resolve。
-
四、测试验证
用以下代码验证手写Promise的核心功能:
// 测试Promise.any
const foo = () => new MyPromise((resolve, reject) => setTimeout(() => reject('foo失败'), 1000))
const bar = () => new MyPromise((resolve, reject) => setTimeout(() => reject('bar失败'), 500))
const baz = () => new MyPromise((resolve) => setTimeout(() => resolve('baz成功'), 1500))
MyPromise.any([foo(), bar(), baz()]).then(
(res) => console.log('any成功:', res), // 输出:baz成功
(err) => console.log('any失败:', err)
)
// 测试allSettled
MyPromise.allSettled([foo(), bar(), baz()]).then((res) => {
console.log('allSettled结果:', res)
// 输出:[{status:'rejected',reason:'foo失败'}, {status:'rejected',reason:'bar失败'}, {status:'fulfilled',value:'baz成功'}]
})
// 测试finally
baz().finally(() => console.log('finally执行')).then(res => console.log(res))
// 输出:finally执行 → baz成功
五、面试高频考点
-
Promise的状态变更机制:为什么状态不可逆?(构造函数中只有
pending状态下才会修改状态); -
then的链式调用原理:
then返回新Promise,回调返回Promise时会“透传”状态; -
all/race/any/allSettled的区别:重点区分“失败处理逻辑”和“返回结果格式”;
-
finally的特性:回调无参数、返回Promise的状态规则、执行时机。
总结
Promise的核心是“状态管理+异步回调队列”,所有扩展方法都是基于这个核心的封装。通过手写Promise,不仅能深刻理解异步编程的本质,还能在面试中从容应对Promise相关的深度问题。掌握这些原理后,再看async/await会更加轻松,真正做到“知其然,更知其所以然”。