手写Promise完整版(含核心原理+误区解析)
本文将从Promise核心知识入手,循序渐进讲解Promise的实现逻辑,包含完整可运行的手写代码,重点解析异步调用、链式调用的核心原理,同时纠正学习过程中容易出现的认知误区(结合自身学习疑惑),帮助彻底吃透Promise底层。 Promise 是JavaScript中用于处理异步操作的对象,核心解决「回调地狱」问题,将异步操作的「成功/失败」结果以统一的方式管理,支持链式调用,让异步代码更简洁、可维护。 Promise有且只有三种状态,状态一旦变更,永久不可修改,这是Promise的核心特性之一: ⚠️ 关键:状态只能从「pending」转向「fulfilled」或「rejected」,不能反向,也不能从fulfilled/rejected转向其他状态。 Promise的then、catch、finally回调均为「微任务」,优先级高于setTimeout等「宏任务」,执行顺序遵循: 同步代码 → 所有微任务 → 一个宏任务 → 所有微任务 → 一个宏任务...(循环) 这也是为什么原生Promise的then回调永远是异步执行,即便同步resolve,也不会立刻执行then回调。 Promise的本质是「状态管理+回调缓存+异步触发」,核心逻辑可总结为3点: 以下代码包含Promise所有核心功能:状态管理、异步支持、链式调用、微任务、catch/finally、static方法(resolve/reject/all/race),结构严格按照要求组织: 最基础的Promise,只实现状态管理和同步resolve/reject,不支持异步和链式调用: ⚠️ 问题:异步场景(如setTimeout内resolve)会失效,因为then执行时,状态还是pending,没有缓存回调,回调会丢失。 核心解决「异步场景回调丢失」问题:pending状态时,将then回调存入队列,等resolve/reject触发后,批量执行队列: ✅ 关键理解:此时存入队列的是「匿名函数」,只是「保存函数」,不执行;只有当resolve/reject触发,才会遍历队列执行函数(异步场景生效)。 比如:setTimeout内resolve,then执行时状态是pending,回调被存入队列,1秒后resolve触发,才执行队列中的回调。 链式调用的核心的是「then返回新的Promise」+「手动触发下一个Promise的resolve/reject」,这也是我之前容易误解的点,重点解析: ❌ 错误认知:所有then的this.status都是第一个Promise的状态,链式调用靠第一个Promise的状态驱动。 ❌ 错误认知:回调队列是链式调用的核心,没有队列就不能链式。 ✅ 正确认知: 以异步场景为例,拆解链式调用的完整流程: ✅ 关键:p2的状态不是天生fulfilled,是p1的then回调中,通过nextResolve手动触发的;没有nextResolve,链式会彻底断掉(这是我之前debug发现的核心真相)。 原生Promise的then回调永远是微任务,即便同步resolve,也不会立刻执行,而是等同步代码执行完再执行。我们通过queueMicrotask实现这一特性: 测试微任务优先级: 这部分都是基于then的语法糖,难度不大,核心逻辑: 手写Promise的核心,不在于代码有多复杂,而在于理解「状态管理」「回调缓存」「链式接力」「微任务异步」这四个关键点。其中,链式调用的灵魂是「nextResolve/nextReject」,队列是异步场景的补充,二者结合,才能实现和原生一致的Promise功能。一、Promise核心前置知识(必懂)
1.1 Promise的核心作用
1.2 Promise的三大核心状态(不可逆)
1.3 事件循环与微任务(Promise异步核心)
1.4 Promise的核心逻辑梳理
二、手写Promise完整版代码(全量可运行)
class MyPromise {
// 三大核心状态(静态常量,所有实例共享)
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
// 构造函数:接收执行器(executor),立即执行
constructor(executor) {
// 实例状态、成功结果、失败原因
this.status = MyPromise.PENDING
this.value = undefined
this.reason = undefined
// 回调队列:缓存pending状态时的then回调(成功/失败)
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
// 成功回调:变更状态,触发成功队列
const resolve = (value) => {
// 状态不可逆:只有pending才能变更为fulfilled
if (this.status !== MyPromise.PENDING) return
this.status = MyPromise.FULFILLED
this.value = value
// 微任务:批量执行成功回调队列(对齐原生异步行为)
queueMicrotask(() => {
this.onFulfilledCallbacks.forEach(fn => fn())
this.onFulfilledCallbacks = [] // 执行后清空队列,避免重复执行
})
}
// 失败回调:变更状态,触发失败队列
const reject = (reason) => {
// 状态不可逆:只有pending才能变更为rejected
if (this.status !== MyPromise.PENDING) return
this.status = MyPromise.REJECTED
this.reason = reason
// 微任务:批量执行失败回调队列
queueMicrotask(() => {
this.onRejectedCallbacks.forEach(fn => fn())
this.onRejectedCallbacks = []
})
}
// 执行器异常处理:执行器抛出错误,直接触发reject
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
// then方法:核心链式调用方法,返回新Promise
then(onFulfilled, onRejected) {
// 缺省兜底:实现值穿透、错误穿透(不传回调时,默认传递结果/错误)
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
// 链式核心:每一个then都返回新的Promise,确保可以连续调用
return new MyPromise((nextResolve, nextReject) => {
// 封装成功回调处理逻辑(复用代码)
const handleFulfilled = () => {
try {
// 执行当前then的成功回调,获取返回值
const res = onFulfilled(this.value)
// 如果返回值是Promise,等待其完成后,再触发下一个Promise
if (res instanceof MyPromise) {
res.then(nextResolve, nextReject)
} else {
// 普通值,直接触发下一个Promise的成功
nextResolve(res)
}
} catch (err) {
// 回调执行出错,触发下一个Promise的失败
nextReject(err)
}
}
// 封装失败回调处理逻辑(复用代码)
const handleRejected = () => {
try {
// 执行当前then的失败回调,获取返回值
const res = onRejected(this.reason)
// 如果返回值是Promise,等待其完成后,再触发下一个Promise
if (res instanceof MyPromise) {
res.then(nextResolve, nextReject)
} else {
// 普通值,直接触发下一个Promise的成功(错误穿透后,后续then可正常接收)
nextResolve(res)
}
} catch (err) {
nextReject(err)
}
}
// 根据当前Promise的状态,执行对应逻辑
if (this.status === MyPromise.FULFILLED) {
// 已成功:微任务执行成功回调(对齐原生异步)
queueMicrotask(handleFulfilled)
} else if (this.status === MyPromise.REJECTED) {
// 已失败:微任务执行失败回调
queueMicrotask(handleRejected)
} else {
// pending状态:将回调存入队列,等待resolve/reject触发
this.onFulfilledCallbacks.push(handleFulfilled)
this.onRejectedCallbacks.push(handleRejected)
}
})
}
// catch方法:错误捕获,本质是then的语法糖(不传成功回调,只传失败回调)
catch(onRejected) {
return this.then(null, onRejected)
}
// finally方法:无论成功/失败,都会执行,不改变链式传递的值
finally(cb) {
return this.then(
val => MyPromise.resolve(cb()).then(() => val), // 成功时,执行cb后传递原值
err => MyPromise.resolve(cb()).then(() => { throw err }) // 失败时,执行cb后抛出原错误
)
}
// 静态resolve方法:快速创建一个已成功的Promise
static resolve(value) {
return new MyPromise(resolve => resolve(value))
}
// 静态reject方法:快速创建一个已失败的Promise
static reject(reason) {
return new MyPromise((_, reject) => reject(reason))
}
// 静态all方法:接收数组,所有Promise成功才返回,一个失败则整体失败
static all(promises) {
return new MyPromise((resolve, reject) => {
// 校验参数:必须是数组
if (!Array.isArray(promises)) {
return reject(new TypeError('Promise.all() 接收的参数必须是数组'))
}
const result = [] // 存储所有成功结果(顺序与传入一致)
let resolvedCount = 0 // 计数:已成功的Promise数量
// 空数组直接resolve(原生行为)
if (promises.length === 0) {
return resolve(result)
}
// 遍历所有Promise,统一处理(普通值会被MyPromise.resolve包装)
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(res => {
result[index] = res // 按传入顺序存储结果
resolvedCount++ // 成功计数+1
// 所有Promise都成功,才resolve结果数组
if (resolvedCount === promises.length) {
resolve(result)
}
}).catch(err => {
// 只要有一个失败,立即reject(整体失败)
reject(err)
})
})
})
}
// 静态race方法:接收数组,谁先完成(成功/失败),就返回谁的结果
static race(promises) {
return new MyPromise((resolve, reject) => {
// 校验参数:必须是数组
if (!Array.isArray(promises)) {
return reject(new TypeError('Promise.race() 接收的参数必须是数组'))
}
// 空数组:永远pending(原生行为)
if (promises.length === 0) {
return
}
// 遍历所有Promise,谁先完成,就触发对应的resolve/reject
promises.forEach(promise => {
MyPromise.resolve(promise).then(resolve).catch(reject)
})
})
}
}
三、循序渐进实现Promise(从基础到完整)
3.1 第一步:实现基础状态管理(同步场景)
class MyPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.status = MyPromise.PENDING
this.value = undefined
this.reason = undefined
const resolve = (value) => {
if (this.status !== MyPromise.PENDING) return
this.status = MyPromise.FULFILLED
this.value = value
}
const reject = (reason) => {
if (this.status !== MyPromise.PENDING) return
this.status = MyPromise.REJECTED
this.reason = reason
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
// 基础then方法:只处理同步状态
then(onFulfilled, onRejected) {
if (this.status === MyPromise.FULFILLED) {
onFulfilled(this.value)
}
if (this.status === MyPromise.REJECTED) {
onRejected(this.reason)
}
// 此时未处理pending状态(异步场景会失效)
}
}
3.2 第二步:加入回调队列(支持异步场景)
class MyPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.status = MyPromise.PENDING
this.value = undefined
this.reason = undefined
// 新增:回调队列,缓存pending状态的then回调
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) => {
if (this.status !== MyPromise.PENDING) return
this.status = MyPromise.FULFILLED
this.value = value
// 新增:状态变更后,批量执行成功回调队列
this.onFulfilledCallbacks.forEach(fn => fn())
}
const reject = (reason) => {
if (this.status !== MyPromise.PENDING) return
this.status = MyPromise.REJECTED
this.reason = reason
// 新增:批量执行失败回调队列
this.onRejectedCallbacks.forEach(fn => fn())
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
if (this.status === MyPromise.FULFILLED) {
onFulfilled(this.value)
}
if (this.status === MyPromise.REJECTED) {
onRejected(this.reason)
}
// 新增:pending状态,将回调存入队列(关键!)
if (this.status === MyPromise.PENDING) {
this.onFulfilledCallbacks.push(() => {
onFulfilled(this.value)
})
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
}
}
}
3.3 第三步:实现链式调用(核心难点)
3.3.1 核心误区纠正(我之前的错误认知)
3.3.2 链式调用核心代码实现
then(onFulfilled, onRejected) {
// 缺省兜底(可选,优化体验)
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
// 链式核心1:返回新的Promise
return new MyPromise((nextResolve, nextReject) => {
const handleFulfilled = () => {
try {
const res = onFulfilled(this.value)
// 链式核心2:手动触发下一个Promise的resolve(传递结果)
nextResolve(res)
} catch (err) {
nextReject(err)
}
}
const handleRejected = () => {
try {
const res = onRejected(this.reason)
nextResolve(res)
} catch (err) {
nextReject(err)
}
}
if (this.status === MyPromise.FULFILLED) {
handleFulfilled()
} else if (this.status === MyPromise.REJECTED) {
handleRejected()
} else {
// pending状态:缓存的是封装后的handleFulfilled/handleRejected
this.onFulfilledCallbacks.push(handleFulfilled)
this.onRejectedCallbacks.push(handleRejected)
}
})
}
3.3.3 链式调用执行流程(彻底看懂)
// 1. 创建第一个Promise(p1),异步resolve
const p1 = new MyPromise((resolve) => {
setTimeout(() => resolve(100), 1000)
})
// 2. p1.then() → 返回新Promise(p2)
const p2 = p1.then(res => {
console.log('第一层then:', res) // 100
return res + 10 // 返回普通值,传递给p2
})
// 3. p2.then() → 返回新Promise(p3)
p2.then(res => {
console.log('第二层then:', res) // 110
})
3.4 第四步:加入微任务(对齐原生异步行为)
// resolve中加入微任务
const resolve = (value) => {
if (this.status !== MyPromise.PENDING) return
this.status = MyPromise.FULFILLED
this.value = value
// 新增:微任务执行回调队列
queueMicrotask(() => {
this.onFulfilledCallbacks.forEach(fn => fn())
this.onFulfilledCallbacks = []
})
}
// then中,已fulfilled/rejected状态也加入微任务
if (this.status === MyPromise.FULFILLED) {
queueMicrotask(handleFulfilled)
} else if (this.status === MyPromise.REJECTED) {
queueMicrotask(handleRejected)
}
console.log('同步开始')
MyPromise.resolve().then(() => console.log('Promise微任务'))
setTimeout(() => console.log('setTimeout宏任务'), 0)
console.log('同步结束')
// 输出顺序:同步开始 → 同步结束 → Promise微任务 → setTimeout宏任务(和原生一致)
3.5 第五步:补充catch/finally和静态方法
四、核心总结(重中之重)
4.1 Promise核心逻辑
4.2 最容易踩的误区(我亲身经历)
4.3 最终结论
五、测试用例(验证代码可用性)
// 1. 异步链式 + 错误穿透 + finally
new MyPromise((resolve) => {
setTimeout(() => resolve(100), 1000)
})
.then(res => {
console.log('第一层:', res) // 100
return res + 200
})
.then(res => {
console.log('第二层:', res) // 300
throw new Error('手动报错')
})
.catch(err => {
console.log('捕获错误:', err.message) // 手动报错
})
.finally(() => {
console.log('finally必定执行')
})
// 2. Promise.all测试(全部成功)
const p1 = MyPromise.resolve(1)
const p2 = new MyPromise(resolve => setTimeout(() => resolve(2), 500))
const p3 = 3 // 普通值会被包装
MyPromise.all([p1, p2, p3]).then(res => {
console.log('all成功:', res) // [1, 2, 3]
})
// 3. Promise.race测试(取最快结果)
const race1 = new MyPromise(resolve => setTimeout(() => resolve('慢'), 1000))
const race2 = new MyPromise(resolve => setTimeout(() => resolve('快'), 100))
MyPromise.race([race1, race2]).then(res => {
console.log('race胜利:', res) // 快
})
// 4. 微任务优先级测试
console.log('同步开始')
MyPromise.resolve().then(() => console.log('Promise微任务'))
setTimeout(() => console.log('setTimeout宏任务'), 0)
console.log('同步结束')
(注:文档部分内容可能由 AI 生成)