理解本文的实现逻辑需要掌握Promise及其使用方法。
搭建初始结构
这里我们采用“类”搭建Promise的初始结构。我们创建Promise对象时,一般写法let p = new Promise((resolve,reject)=>{resolve(222)})。我们输出p看一下。
我们首先在构造函数中声明并执行创建对象时的(resolve,reject)=>{resolve(222)}这个回调函数。
class CustomPromise {
constructor(initFunc) {
initFunc()
}
}
这个时候如果我们创建CustomPromise的实例对象会报错。因为resolve,reject两个入参我们没有声明,在执行到resolve(222)时报错。下面进行声明:
class CustomPromise {
constructor(initFunc) {
initFunc(this.resolve, this.reject)
}
resolve(value) {}
reject(value) { }
}
这个时候我们执行let cp = new CustomPromise((resolve, reject) => { resolve(222) })这段代码看一下:
已经不报错了,非常的棒。但是此时的cp是没有任何属性的空对象。我们把Promise的三个状态:等待状态pending、成功状态fulfilled、失败状态rejected和值声明出来。
class CustomPromise {
constructor(initFunc) {
this.status = 'pending'
this.result = undefined
initFunc(this.resolve, this.reject)
}
resolve(value) {}
reject(value) {}
}
我们看一下目前cp打印出来的值:
目前,基本架构搭建完成。
搭建resolve和reject方法
PromiseA+规范中说明,Promise状态只能通过pending转为fulfilled或rejected。我们需要在resolve和reject函数中增加此逻辑。这两个函数主要功能是改变Promise的状态和设置Promise的值。我们来实现:
class CustomPromise {
constructor(initFunc) {
this.status = 'pending'
this.result = undefined
initFunc(this.resolve.bind(this), this.reject.bind(this))
}
resolve(value) {
if (this.status !== 'pending') return
this.status = 'fulfilled'
this.result = value
}
reject(value) {
if (this.status !== 'pending') return
this.status = 'rejected'
this.result = value
}
}
因为initFunc函数内参数是回调函数,所以resolve和reject函数只能手动声明this,我们通过bind手动绑定。这里不能通过call进行绑定,因为resolve函数是用户调用的(当执行resolve(222)时,才执行此函数)。目前cp的打印出来的值为:
这里在初始化对象时,执行的回调函数resolve(CustomPromise中的initFunc执行时要处理异常情况,所以需要用try...catch处理)
至此,初始结构丰满完成,已经初具人形了。
搭建then方法
我们平时使用Promise时:
let p = new Promise((resolve, reject) => { resolve(222) })
let pp = p
.then(
res => {
console.log(res, 'pp-res')
},
err => {
console.log(err, 'pp-err')
}
)
大概像这样,会输出:(这段Promise的逻辑输出后面文章也会输出,代码后面就不放了。)
所以我们看到Promise的then函数会实现的基础功能为:
- 接收两个回调函数
- 根据p不同的状态调用不同的函数
class CustomPromise {
constructor(initFunc) {
this.status = 'pending'
this.result = undefined
try {
initFunc(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(value) {
if (this.status !== 'pending') return
this.status = 'fulfilled'
this.result = value
}
reject(value) {
if (this.status !== 'pending') return
this.status = 'rejected'
this.result = value
}
then(onFulfilled, onRejected) {
if (this.status === 'fulfilled') {
onFulfilled(this.result)
}
if (this.status === 'rejected') {
onRejected(this.result)
}
}
}
这样就实现了then的基本逻辑。
兼容异步任务
上面我们封装CustomPromise都是基于CustomPromise同步创建,这样initFunc函数(初始回调函数)>resolve函数(改变状态和值)>then函数可以同步执行,如果异步创建呢?我们执行这段程序。
let cp = new CustomPromise((resolve, reject) => {
setTimeout(() => {
resolve(222)
}, 1000);
})
console.log(cp)
cp.then(res => {
console.log(res, '1')
})
let p = new Promise((resolve, reject) => { resolve(222) })
let pp = p
.then(
res => {
console.log(res, 'pp-res')
},
err => {
console.log(err, 'pp-err')
}
)
上图的输出结果中,我们看到输出的cp的状态为pending,then函数的回调函数没有执行。
为啥子呢?一开始创建cp时,status初始状态就是pending,reault值为undefined。这个时候resolve函数在宏任务中,被排到了下一次事件循环。接着就是执行同步任务输出cp,即为pending状态。接着是执行then函数,此时status的状态还是pending,在我们目前搭建的CustomPromise中,并没有对此状态的处理。
因此,增加处理逻辑,把此状态下then的回调函数保存起来,等待resolve或reject函数内容执行完成后再执行。
class CustomPromise {
constructor(initFunc) {
this.status = 'pending'
this.result = undefined
this.onFulfilledFunc = undefined
this.onRejectedFunc = undefined
try {
initFunc(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(value) {
if (this.status !== 'pending') return
this.status = 'fulfilled'
this.result = value
this.onFulfilledFunc && this.onFulfilledFunc(this.result)
}
reject(value) {
if (this.status !== 'pending') return
this.status = 'rejected'
this.result = value
this.onRejectedFunc && this.onRejectedFunc(this.result)
}
then(onFulfilled, onRejected) {
if (this.status === 'fulfilled') {
onFulfilled(this.result)
}
if (this.status === 'rejected') {
onRejected(this.result)
}
if (this.status === 'pending') {
this.onFulfilledFunc = onFulfilled
this.onRejectedFunc = onRejected
}
}
}
这里我们看到上面程序中CustomPromis的then函数输出也在最后输出了出来。所以如果onFulfilledFunc或onRejectedFunc有值的话,就说明创建CustomPromise时,是异步创建。此逻辑的处理相当于延后then函数中回调函数的执行。
另外,PromiseA+规范中说明,
- then函数内参数必须为函数类型
- then函数支持多次调用
- then函数应该返回Promise
then函数内参数必须为函数类型的要求我们在then函数中判断一下即可。then函数支持多次调用这个要求我们看一下:
let cp = new CustomPromise((resolve, reject) => {
// setTimeout(() => {
resolve(222)
// }, 1000);
})
cp.then(res => {
console.log(res, 'cp-res')
})
cp.then(res => {
console.log(res, '1')
})
let p = new Promise((resolve, reject) => { resolve(222) })
let pp = p
.then(
res => {
console.log(res, 'pp-res')
},
err => {
console.log(err, 'pp-err')
}
)
从上图中看到,同步创建cp对象时,执行时没啥问题。如果把setTimeout函数注释取消呢?
我们发现,Promise那段逻辑输出的pp-res在前,1这个在后了,顺序正确。但是cp-res的输出内容消失了!为什么呢?在把setTimeout放开注释后,我们来看下执行流程:
- 创建CustomPromise对象cp,状态为pending、值为undefined,
- 执行第一个then函数,判断状态为pending,保存回调函数至onFulfilledFunc
- 执行第二个then函数,判断状态为pending,替换回调函数onFulfilledFunc!
- setTimeout函数到主线程执行,执行完成后执行替换后的onFulfilledFunc函数。
所以,为了支持多次调用,我们需要把保存的变量修改为数组。
class CustomPromise {
constructor(initFunc) {
this.status = 'pending'
this.result = undefined
this.onFulfilledFuncList = []
this.onRejectedFuncList = []
try {
initFunc(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(value) {
if (this.status !== 'pending') return
this.status = 'fulfilled'
this.result = value
if (this.onFulfilledFuncList.length > 0) {
this.onFulfilledFuncList.forEach(func => func(this.result))
}
}
reject(value) {
if (this.status !== 'pending') return
this.status = 'rejected'
this.result = value
if (this.onRejectedFuncList.length > 0) {
this.onRejectedFuncList.forEach(func => func(this.result))
}
}
then(onFulfilled, onRejected) {
// 判断入参是否为函数
let realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
let realOnRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }
if (this.status === 'fulfilled') {
realOnFulfilled(this.result)
}
if (this.status === 'rejected') {
realOnRejected(this.result)
}
// 支持多次调用then函数
if (this.status === 'pending') {
this.onFulfilledFuncList.push(onFulfilled)
this.onRejectedFuncList.push(onRejected)
}
}
}
如此,这般。
then函数返回Promise
同步情况
在then函数执行时,回调函数可能返回
- CustomPromise
- 原始数据类型或引用数据类型
- 异常
- 无回调函数
我们依次看怎么处理
- CustomPromise:直接返回即可
- 原始数据类型或引用数据类型:通过CustomPromise返回,值为返回值,状态为fulfilled(resolve)
- 异常:通过CustomPromise返回,值为异常值,状态为rejected(reject)
- 无回调函数:通过CustomPromise返回,状态和值继承于调用then函数的CustomPromise对象(相当于没走then函数,直接返回)
then(onFulfilled, onRejected) {
// 判断入参是否为函数
let realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
let realOnRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }
return new CustomPromise((resolve, reject) => {
if (this.status === 'fulfilled') {
try {
// 回调函数的值
let value = realOnFulfilled(this.result)
if (value instanceof CustomPromise) {
// 执行后调用回调函数,到最外层返回
value.then(res => resolve(res), err => reject(err))
} else {
resolve(value)
}
} catch (error) {
// 处理异常
reject(error)
}
}
if (this.status === 'rejected') {
try {
// 回调函数的值
let value = realOnRejected(this.result)
if (value instanceof CustomPromise) {
// 执行后调用回调函数,到最外层返回
value.then(res => resolve(res), err => reject(err))
} else {
resolve(value)
}
} catch (error) {
// 处理异常
reject(error)
}
}
// 支持多次调用then函数
if (this.status === 'pending') {
this.onFulfilledFuncList.push(onFulfilled)
this.onRejectedFuncList.push(onRejected)
}
})
}
执行此段逻辑
let cp = new CustomPromise((resolve, reject) => {
resolve(222)
})
let cp2 = cp.then(res => {
console.log(res, 'cp-res')
})
console.log(cp2, 'cp2')
let p = new Promise((resolve, reject) => { resolve(222) })
let pp = p
.then(
res => {
console.log(res, 'pp-res')
},
err => {
console.log(err, 'pp-err')
}
)
-
这里then的回调函数没有返回,其实是按undefined处理(原始数据类型)
-
如果then返回CustomPromise类型,执行CustomPromise中then方法第12行
value.then(res => resolve(res), err => reject(err))此时value即为CustomPromise类型。目前处理是执行value对象的then方法,通过第五行统一封装返回。ps.也可以每种情况单独返回,不通过第五行统一返回。那么第一种情况"then的回调函数没有返回"这么写
return new Promise(resolve=>resolve(value)),如果是CustomPromise类型,直接返回return value。异常捕获这么写return new Promise((resolve,reject)=>reject(value)) -
异常通过trycatch捕获
-
无回调函数:此时判断then函数是否为函数的第三、四行为空时的默认函数,就是返回的调用then函数的CustomPromise对象的值。
异步情况
异步和同步的逻辑相同,处理方式略有不同
then(onFulfilled, onRejected) {
// 判断入参是否为函数
let realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
let realOnRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }
return new CustomPromise((resolve, reject) => {
if (this.status === 'fulfilled') {
try {
let value = realOnFulfilled(this.result)
if (value instanceof CustomPromise) {
// 执行后调用回调函数,到最外层返回
value.then(res => resolve(res), err => reject(err))
} else {
resolve(value)
}
} catch (error) {
// 处理异常
reject(error)
}
}
if (this.status === 'rejected') {
try {
let value = realOnRejected(this.result)
if (value instanceof CustomPromise) {
// 执行后调用回调函数,到最外层返回
value.then(res => resolve(res), err => reject(err))
} else {
resolve(value)
}
} catch (error) {
// 处理异常
reject(error)
}
}
// 支持多次调用then函数
if (this.status === 'pending') {
this.onFulfilledFuncList.push((result) => {
try {
let value = realOnFulfilled(result)
if (value instanceof CustomPromise) {
// 执行后调用回调函数,到最外层返回
value.then(res => resolve(res), err => reject(err))
} else {
resolve(value)
}
} catch (error) {
// 处理异常
reject(error)
}
})
this.onRejectedFuncList.push((result) => {
try {
let value = realOnRejected(result)
if (value instanceof CustomPromise) {
// 执行后调用回调函数,到最外层返回
value.then(res => resolve(res), err => reject(err))
} else {
resolve(value)
}
} catch (error) {
// 处理异常
reject(error)
}
})
}
})
}
这里把then函数的回调函数封装起来,执行后进行返回。result作为入参,是在resolve或reject函数执行时,传入的参数。
方法封装
现在我们把返回CustomPromise这段公共代码封装起来
then(onFulfilled, onRejected) {
// 判断入参是否为函数
let realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
let realOnRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }
return new CustomPromise((resolve, reject) => {
if (this.status === 'fulfilled') {
try {
let value = realOnFulfilled(this.result)
this.handleThenReturnPromise(value, resolve, reject)
} catch (error) {
// 处理异常
reject(error)
}
}
if (this.status === 'rejected') {
try {
let value = realOnRejected(this.result)
this.handleThenReturnPromise(value, resolve, reject)
} catch (error) {
// 处理异常
reject(error)
}
}
// 支持多次调用then函数
if (this.status === 'pending') {
this.onFulfilledFuncList.push((result) => {
try {
let value = realOnFulfilled(result)
this.handleThenReturnPromise(value, resolve, reject)
} catch (error) {
// 处理异常
reject(error)
}
})
this.onRejectedFuncList.push((result) => {
try {
let value = realOnRejected(result)
this.handleThenReturnPromise(value, resolve, reject)
} catch (error) {
// 处理异常
reject(error)
}
})
}
})
}
handleThenReturnPromise(value, resolve, reject) {
if (value instanceof CustomPromise) {
// 执行后调用回调函数,到最外层返回
value.then(res => resolve(res), err => reject(err))
} else {
resolve(value)
}
}
网上有很多方法直接封装成resolvePromise方法,处理了一些公共业务,我们只针对CustomPromise这一种情况,再加上对then函数返回的是嵌套CustomPromise的递归逻辑处理。
handleThenReturnPromise(value, resolve, reject) {
if (value instanceof CustomPromise) {
// 执行后调用回调函数,到最外层返回
value.then(res => this.handleThenReturnPromise(res, resolve, reject), err => reject(err))
} else {
resolve(value)
}
}
微任务
PromiseA+规范规定onFulfilled和onRejected应该是微任务。Promise中then方法的回调函数输出逻辑也是如此:
let p = new Promise((resolve, reject) => { resolve(222) })
let pp = p.then(res => {
console.log(res, 'res')
})
console.log(pp, 'pp')
这段逻辑输出为:
先执行同步任务(第五行),再执行微任务(then的回调函数)
目前CustomPromise中then的回调函数时同步任务,我们看下执行
let cp = new CustomPromise((resolve, reject) => {
resolve(123)
console.log("after")
})
let cp2 = cp.then(
(res) => {
console.log(res, 'res')
},
(error) => {
console.log(error, 'error')
}
)
console.log('end')
执行结果为:
所以我们使用queueMicrotask把then的回调函数置为微任务。
class CustomPromise {
constructor(initFunc) {
this.status = 'pending'
this.result = undefined
this.onFulfilledFuncList = []
this.onRejectedFuncList = []
try {
initFunc(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(value) {
if (this.status !== 'pending') return
this.status = 'fulfilled'
this.result = value
if (this.onFulfilledFuncList.length > 0) {
queueMicrotask(() => {
this.onFulfilledFuncList.forEach(func => func(this.result))
})
}
}
reject(value) {
if (this.status !== 'pending') return
this.status = 'rejected'
this.result = value
if (this.onRejectedFuncList.length > 0) {
queueMicrotask(() => {
this.onRejectedFuncList.forEach(func => func(this.result))
})
}
}
then(onFulfilled, onRejected) {
// 判断入参是否为函数
let realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
let realOnRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }
return new CustomPromise((resolve, reject) => {
if (this.status === 'fulfilled') {
queueMicrotask(() => {
try {
let value = realOnFulfilled(this.result)
this.handleThenReturnPromise(value, resolve, reject)
} catch (error) {
// 处理异常
reject(error)
}
})
}
if (this.status === 'rejected') {
queueMicrotask(() => {
try {
let value = realOnRejected(this.result)
this.handleThenReturnPromise(value, resolve, reject)
} catch (error) {
// 处理异常
reject(error)
}
})
}
// 支持多次调用then函数
if (this.status === 'pending') {
this.onFulfilledFuncList.push((result) => {
try {
let value = realOnFulfilled(result)
this.handleThenReturnPromise(value, resolve, reject)
} catch (error) {
// 处理异常
reject(error)
}
})
this.onRejectedFuncList.push((result) => {
try {
let value = realOnRejected(result)
this.handleThenReturnPromise(value, resolve, reject)
} catch (error) {
// 处理异常
reject(error)
}
})
}
})
}
handleThenReturnPromise(value, resolve, reject) {
if (value instanceof CustomPromise) {
// 执行后调用回调函数,到最外层返回
value.then(res => this.handleThenReturnPromise(res, resolve, reject), err => reject(err))
} else {
resolve(value)
}
}
catch(onRejected) {
return this.then(null, onRejected)
}
}
在then函数的回调函数执行时添加到微任务队列。此时的输出结果为:
搭建catch方法
我们可以通过调用then函数,把catch的回调函数作为第二个参数传入。
catch(onRejected) {
return this.then(null, onRejected)
}
测试一下:
let cp = new CustomPromise((resolve, reject) => {
reject(123)
})
cp.catch(err => {
console.log(err, 'err')
})
- 创建CustomPromise对象后,cp为值为123,状态为rejected的对象;
- 执行catch方法,走then方法中
status==="rejected"的逻辑返回resolve(123)。
let cp = new CustomPromise((resolve, reject) => {
reject(123)
})
cp
.then(res => {
console.log(res, 'res')
})
.catch(err => {
console.log(err, 'err')
})
这段代码相较于上段代码增加了一段then。两段代码的执行结果相同。
(1)创建CustomPromise对象后,cp为值为123,状态为rejected的对象;
(2)执行then函数,判断状态status==="rejected",但是入参中第二个参数onRejected参数为空,赋值为默认方法error => { throw error },抛出异常后被try...catch捕获,返回CustomPromise对象,状态为reject,值为123。
(3)执行catch方法,走then方法中status==="rejected"的逻辑返回resolve(123)。
搭建CustomPromise.resolve()和CustomPromise.reject()方法
类似Promise.resolve()和Promise.reject()方法,可以直接通过类名调用,返回Promise。
static resolve(value) {
if (value instanceof CustomPromise) {
return value
} else {
return new CustomPromise(resolve => resolve(value))
}
}
static reject(value) {
return new CustomPromise((resolve, reject) => reject(value))
}
声明静态方法,可以直接调用。
搭建finally方法
finally方法它是保证在Promise调用链的哪一节都会执行并且向后传递调用它的Promise对象。
finally(cb) {
// 返回CustomPromise
return this.then(
// 如果调用finally方法的CustomPromise状态为fulfilled,调用此方法
(value) => {
// 首先保证finally方法中的回调函数执行完成
// 返回调用finally方法的CustomPromise的值给后续链使用
return CustomPromise.resolve(cb()).then(() => value)
},
// 如果调用finally方法的CustomPromise状态为rejected,调用此方法
(value) => {
return CustomPromise.resolve(cb()).then(() => { throw value })
}
)
}
最后还有一些方法像all、race等方法就不多介绍了,如果感兴趣可以点这里看一下,非常详细。
写文不易,点赞鼓励一下吧!
参考:juejin.cn/post/748079… zhuanlan.zhihu.com/p/183801144 zhuanlan.zhihu.com/p/451254515