关于手写实现一个promise,网上有很多相关的教程,这里推荐一篇文章Promise实现原理(附源码) - 简书,这片文章对promise的实现过程讲解的很明白
1. 我们先来看下面这个代码,这个代码没有严格遵守Promise/A+规范,就是一个简易版的promise,这个代码可以更好理解promise原理(直逼原理)
- promise代码
class MyPromise {
status = undefined
value = undefined
reason = undefined
successCallBack = []
failCallBack = []
constructor(executor) {
this.status = 'pending'
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
resolve = (value) => {
if (this.status !== 'pending') return
this.status = 'fulfilled'
this.value = value
while (this.successCallBack.length) {
// shift()方法删除数组中第一项,并返回改项
this.successCallBack.shift()()
}
}
reject = (reason) => {
if (this.status !== 'pending') return
this.status = 'rejected'
this.reason = reason
while (this.failCallBack.length) {
this.failCallBack.shift()()
}
}
then(successCallBack, failCallBack) {
// successCallback || failCallBack 不存在
// 为什么要储存回掉函数?看下面这段代码
// let p = new MyPromise((resolve, reject) => {
// setTimeout(() => {
// resolve(1)
// })
// }).then(res => do something...)
// 这时候,因为resolve在异步队列中,他会在本轮时间循环结束后执行,所以这时候的status为pending,
// 如果传入的为同步的方法,可以理解为上面代码中没有setTimeout,这时status为fulfilled或者rejected
successCallBack = successCallBack ? successCallBack : value => value
failCallBack = failCallBack ? failCallBack : reason => reason
let p2 = new MyPromise((resolve, reject) => {
if (this.status === 'fulfilled') {
// 如果为同步代码即没有setTimeout则执行状态为fulfilled或者rejected的代码
// 当然同步的时候这里直接执行 successCallBack(this.value)就ok
let x = successCallBack(this.value)
resolvePromise(p2, x, resolve, reject)
} else if (this.status === 'rejected') {
let x = failCallBack(this.reason)
resolvePromise(p2, x, resolve, reject)
} else if (this.status === "pending") {
this.successCallBack.push(() => {
let x = successCallBack(this.value)
resolvePromise(p2, x, resolve, reject)
})
this.failCallBack.push(() => {
let x = failCallBack(this.reason)
resolvePromise(p2, x, resolve, reject)
})
}
})
return p2
}
static resolve(value) {
if (value instanceof MyPromise) return value
return new MyPromise((resolve) => resolve(value))
}
finally = (callBack) => {
return this.then(value => {
return MyPromise.resolve(callBack()).then(() => value)
}, reason => {
return MyPromise.resolve(callBack()).then(() => reason)
})
}
}
function resolvePromise(promise, x, resolve, reject) {
if (promise === x) {
console.log("相同promise")
return
}
if (x instanceof MyPromise) {
// 传递的是一个new myPromise类型
x.then(resolve, reject) // 目的是储存当前传递promise的value值,resolve,reject都是当前promise的实例对象的resolve和reject
} else {
resolve(x)
}
}
export { MyPromise }
- 测试用例
// 同步resolve // let p = new MyPromise((resolve, reject) => { // setTimeout(() => { // resolve(1) // }) // }) // p.then(res => { // return res * 2 // }).then(item => { // return item * 2 // }).then(res => { // console.log(res) // }) // 异步resolve let p = new MyPromise((resolve, reject) => { setTimeout(() => { resolve(1) }) }) p.then(res => { return new MyPromise((resolve, reject) => { console.log(res) // 1 setTimeout(() => { resolve(res * 2) }) }) }).then(item => { return new MyPromise((resolve, reject) => { console.log(item) // 2 setTimeout(() => { resolve(item * 2) }) }) }).then(res => { console.log(res) // 4 })
promise的重点在于promise可以通过.then的方式完成链式调用,所以我们重点关注下then方法:then方法返回了一个promise实例,很多人对于调用resolvePromise这个方法有很多疑惑,这个方法接受4个值,promise:上一个promise实例,x:回掉方法(可能是promise实例或者一个普通的函数),resolve:成功的回掉方法,rejected:失败的回掉方法;
如果promise === x即上一个promise === 当前回掉方法返回的promise实例,则return并暴露一个错误(自己调用自己,用完执行不完),如果x是一个promise实例就调用当前实例的then()方法,此时当前实例的value值(或者rejected)为上一个promise的返回值(注意看测试用例中高亮部分)
为什么要加入successCallBack回掉函数的数组??
当resolve在setTimeout中,即异步方法的时候,代码的执行顺序是先执行了.then()方法,在执行resolve方法。执行.then方法的时候,我们将回掉函数的储存在了回掉数组successCallBan中,等到执行resolve的通过success.shift()()的方法执行各个回掉函数
- 对应的TS代码
enum Status {
PENDING = "pending",
FULFILLED = "fulfilled",
REJECTED = "rejected"
}
class MyPromise {
// 定义变量
status: string | undefined = undefined
value: any | undefined = undefined
reason: any | undefined = undefined
successCallBackArr: Array<() => void> = []
failCallBackArr: Array<() => void> = []
// executor接受两个参数: resolve, reject,返回值void
constructor(executor: (resolve?: (value?: any) => void, reject?: () => void) => void) {
this.status = Status.PENDING
try {
executor(this.resolve, this.reject)
} catch (e) {
this.reject(e)
}
}
resolve = (value?: any): void => {
if (this.status !== Status.PENDING) return
this.status = Status.FULFILLED
this.value = value
while (this.successCallBackArr.length) {
/**
* 不能 this.successCallBackArr.shift()(), 报错
*/
let successCallBackFn = this.successCallBackArr.shift()
if (successCallBackFn) {
successCallBackFn()
}
}
}
reject = (reason?: any): void => {
if (this.status !== Status.PENDING) return
this.status = Status.REJECTED
this.reason = reason
while (this.failCallBackArr.length) {
let failCallBack = this.failCallBackArr.shift()
if (failCallBack) {
failCallBack()
}
}
}
// 传递回调函数,有默认值,是个函数
then(successCallBack: (value: any) => void = value => value, failCallBack: (reason: any) => void = reason => reason): MyPromise {
let p2 = new MyPromise((resolve: (value: any) => void = this.resolve, reject: (reason: any) => void = this.reject) => {
if (this.status === Status.FULFILLED) {
let x = successCallBack(this.value)
resolvePromise(p2, x, resolve, reject)
} else if (this.status === Status.REJECTED) {
let x = failCallBack(this.reason)
resolvePromise(p2, x, resolve, reject)
} else if (this.status === Status.PENDING) {
this.successCallBackArr.push(() => {
let x = successCallBack(this.value)
resolvePromise(p2, x, resolve, reject)
})
this.failCallBackArr.push(() => {
let x = failCallBack(this.reason)
resolvePromise(p2, x, resolve, reject)
})
}
})
return p2
}
catch(failCallBack: (reason: any) => void) {
return this.then(undefined, failCallBack)
}
}
function resolvePromise(p2: MyPromise, x: any, resolve: (value: any) => void, reject: (value: any) => void): void {
if (p2 === x) {
return reject(new TypeError('循环返回相同的peomise对象'))
}
if (x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}
如有不对或理解不周之处,还望指出,如有不懂之处,可以对比文章开头指出的文章对比来看
本文介绍了如何手写一个简易版的Promise类,详细解析了构造函数、resolve、reject方法以及then方法的工作流程。通过示例代码展示了Promise如何处理同步和异步操作,特别强调了在异步场景下回调函数的储存和执行。同时,文章提供了测试用例以加深理解。

770

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



