携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
从0实现一个基本的Promise类(三)
前言
“手写Promise”系列作为前端常考点,深度检查了应聘者对Promise使用以及规范的理解,忏愧的是本人也是最近在完全通关这一模块。
此篇作为复习笔记,以及学习总结。
手写Promise包含以下知识点 👇:
- Promise基础知识
- Class 类
- 改变this指向 (call、apply和bind)
- 事件循环 Event Loop
其中Promise基础知识如果不了解或有遗忘的同学可以参考我这篇文章:《简单明了的Promise基本知识点》
在上文《从0实现一个基本的Promise类(二)》,我们加入了基本的then方法、捕捉异常、参数校验以及实现异步,代码如下
class myPromise{
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(func){
this.PromiseState = myPromise.PENDING
this.PromiseResult = undefined
try{
func(this.resolve.bind(this),this.reject.bind(this))
}catch (e){
this.reject(e)
}
}
resolve(result){
if(this.PromiseState == myPromise.PENDING){
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
}
}
reject(reason){
if (this.PromiseState == myPromise.PENDING){
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
}
}
then(onFulfilled,onRejected){
onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : value => value
onRejected = typeof onRejected == 'function' ? onRejected : reason =>{ throw reason }
if (this.PromiseState == myPromise.FULFILLED){
setTimeout(()=>{
onFulfilled(this.PromiseResult)
})
}else if(this.PromiseState == myPromise.REJECTED){
setTimeout(()=>{
onRejected(this.PromiseResult)
})
}
}
}
本文将讲解如何在**pending状态下执行then方法的回调保存**。
then方法的回调保存
如果当promise状态为pending时,就执行then方法会怎么样呢🤔?
我们用原生的Promise的执行函数中加上定时器延时处理promise状态,这样then方法就会接受到一个pending状态但又随即会改变状态的promise了:
let promise1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('急急急')
},1000)
}).then((res)=>{
console.log(res)
})
原生的Promise在延迟一秒后,打印出了急急急。从这我们可以看出,在pending状态时,then方法会将传入的回调函数进行保存。然后等到状态发生变化时,去执行😶🌫️
接着,用我们之前实现的myPromise试试呢?
class myPromise{
....
}
let promise1 = new myPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('急急急')
},1000)
}).then((res)=>{
console.log(res)
})
结果当然是什么都不会做,我们还未在then方法处理pending状态的情况。
接下来我们考虑当then里面判断到 pending 待定状态时我们要干什么?
首先我们需要用俩个数组一个来保存失败时的回调,一个来保存成功时的回调。
为什么用数组呢?因为原生的promise是能够链式调用then方法的,所以就存在着状态为pending的promise多次被调用then方法的情况。因此我们在构造实例时,给每个实例添加两个属性:
onFulfilledCallbacks:保存成功时的回调onRejectedCallbacks:保存失败时的回调
class myPromise{
....
constructor(func){
this.PromiseState = myPromise.PENDING
this.PromiseResult = undefined
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
try{
func(this.resolve.bind(this),this.reject.bind(this))
}catch (e){
this.reject(e)
}
}
....
}
接着我们需要在then方法中,判定状态为pending时,将传入的回调函数存入到这俩个数组中:
then(onFulfilled,onRejected){
onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : value => value
onRejected = typeof onRejected == 'function' ? onRejected : reason =>{ throw reason }
if (this.PromiseState == myPromise.FULFILLED){
setTimeout(()=>{
onFulfilled(this.PromiseResult)
})
}else if(this.PromiseState == myPromise.REJECTED){
setTimeout(()=>{
onRejected(this.PromiseResult)
})
}else if(this.PromiseState === myPromise.PENDING){
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
}
存入回调数组后,我们就需要在状态处理后去执行这些回调了。也就是在resolve和reject改变状态后,依次执行数组中的每一项回调。
class myPromise{
...
resolve(result){
if(this.PromiseState == myPromise.PENDING){
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
this.onFulfilledCallbacks.forEach(callback=>{callback(result)})
}
}
reject(reason){
if (this.PromiseState == myPromise.PENDING){
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
this.onRejectedCallbacks.forEach(callback=>{callback(reason)})
}
}
...
}
现在我们再来用刚才的案例测试一下:
class myPromise{
....
}
let promise1 = new myPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('急急急')
},1000)
}).then((res)=>{
console.log(res)
})
结果:同样在经过1秒延迟后输出了急急急,成功。不过这里还隐藏着一个问题,resolve是异步执行的,导致本应在then方法中加入异步队列执行的函数,在resolve中直接就执行了。因此我们还需要在resolve以及reject中加上**setTimeout** :
resolve(result){
if(this.PromiseState == myPromise.PENDING){
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
setTimeout(()=>{
this.onFulfilledCallbacks.forEach(callback=>{callback(result)})
})
}
}
reject(reason){
if (this.PromiseState == myPromise.PENDING){
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
setTimeout(()=>{
this.onRejectedCallbacks.forEach(callback=>{callback(reason)})
})
}
}
我们再用一个例子来验证下多次调用then方法:
class myPromise{
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(func){
this.PromiseState = myPromise.PENDING
this.PromiseResult = undefined
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
try{
func(this.resolve.bind(this),this.reject.bind(this))
}catch (e){
this.reject(e)
}
}
resolve(result){
if(this.PromiseState == myPromise.PENDING){
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
setTimeout(()=>{
this.onFulfilledCallbacks.forEach(callback=>{callback(result)})
})
}
}
reject(reason){
if (this.PromiseState == myPromise.PENDING){
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
setTimeout(()=>{
this.onRejectedCallbacks.forEach(callback=>{callback(reason)})
})
}
}
then(onFulfilled,onRejected){
onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : value => value
onRejected = typeof onRejected == 'function' ? onRejected : reason =>{ throw reason }
if (this.PromiseState == myPromise.FULFILLED){
setTimeout(()=>{
onFulfilled(this.PromiseResult)
})
}else if(this.PromiseState == myPromise.REJECTED){
setTimeout(()=>{
onRejected(this.PromiseResult)
})
}else if(this.PromiseState === myPromise.PENDING){
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
}
}
// 测试代码
const promise = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 2000);
})
promise.then(value => {
console.log(1)
console.log('resolve', value)
})
promise.then(value => {
console.log(2)
console.log('resolve', value)
})
promise.then(value => {
console.log(3)
console.log('resolve', value)
})
运行上面 代码,输出结果👇
1
resolve success
2
resolve success
3
resolve success
所以我们已经能够实现一个能够多次调用、保存回调的**then**方法了。然而then方法还有一个最大的部分仍未实现:链式调用。由于这一部分篇幅过于长,我会在后文更新讲解。
Next
由于将整个Promise细节堆在一起讲解篇幅会过长,我将文章也拆分为了五个小段进行描述。目的是为了让大家能够理解到不同的阶段,能够充分的消化并在实战中使用。
下一期从0实现一个基本的Promise类(四)会讲述
- then方法的链式调用