相信大家学了promise后,面试都会遇到手写promise的现象,我也尝试了通过自己的理解来简单的实现promise手写,发现只要将官方的promise整个API执行了解透彻,手写其实还是很好掌握的,一起来看看~
核心逻辑
首先我们需要知道promise的核心逻辑
01 Promise是一个类,在执行类的时候,需要传递一个执行器,只要new了,执行器就会执行
02 Promise中有三种状态,等待Pending,成功Fulfilled,失败Rejectd,一旦确定就不能再更改
03 resolve和reject函数都是用来更改状态的,resolve:fulfilled;reject:rejected
04 then内部做的事情就是判断状态,如果状态是成功的,调用成功的函数,如果状态是失败,则调用失败函数
05 then成功回调有一个参数,表示成功后的值;失败回调会有一个参数,表示失败的原因
//基础的promise手写实现
const PENDING = 'pengding';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
//构造一个执行器
constructor (executor){
executor(this.resolve,this.reject)
}
//promise的状态
status = PENDING;
//成功失败的值
value = undefined
reason = undefined
//定义执行函数
resolve = value =>{
//只有在等待着状态才可以更改状态值
if(this.status !== PENDING) return;
this.status = FULFILLED;
this.value = value
}
reject = reson => {
//只有在等待着状态才可以更改状态值
if(this.status !== PENDING) return;
this.status = REJECTED;
this.reson = reson
}
then (successCallback,failCallback) {
//判断状态然后再执行
if (this.status === FULFILLED){
successCallback(this.value)
} else if(this.status === REJECTED) {
failCallback(this.reson)
}
}
}
module.exports = MyPromise
异步处理
当reject中内容使用定时器时,并不影响主线程then的执行,那么需要将成功失败处理函数暂存起来,等到主线程执行结束处理返回执行宏任务时,直接使用缓存的处理函数执行
多个then的执行
就是将缓存处理函数的所有值都保存在一个数组中,当多个then执行的时候,就从数组中shift()弹出执行,直到数组长度为0
//成功失败的处理函数暂存
successCallback = []
failCallback = []
//定义执行函数
resolve = value =>{
//只有在等待着状态才可以更改状态值
if(this.status !== PENDING) return;
this.status = FULFILLED;
this.value = value
//如果缓存中存在函数,则执行
//this.successCallback && this.successCallback(this.value)
while(this.successCallback.length) this.successCallback.shift()(this.value)
}
reject = reason => {
//只有在等待着状态才可以更改状态值
if(this.status !== PENDING) return;
this.status = REJECTED;
this.reason = reason
// this.failCallback && this.failCallback(this.reason)
while(this.failCallback.length) this.failCallback.shift()(this.reason)
}
then的链式调用
01 首先必须返回一个promise对象后才能再次then调用
02 需要将第一次返回的结果作为第二次then调用的参数
03 将每次的执行结果保存在变量中,作为resolve执行函数的参数
04 如果返回的是一个普通值直接执行,如果是继续是一个promise对象,那么就需要对它的结果做判断再去执行
识别自返回
在原封装的promise函数中,不允许then回调执行后返回自己,所以我们需要单独添加一个判断条件,存在问题在于执行过程中无法直接拿到promise结果,就需要我们通过异步实现
then (successCallback,failCallback) {
successCallback = successCallback ? successCallback : this.value = this.value ;
failCallback = failCallback ? failCallback : this.reason = this.reason ;
let promise2 = new MyPromise((resolve,reject) => {
//判断状态然后再执行
if (this.status === FULFILLED){
let x = successCallback(this.value)
//通过异步实现识别promise对象自返回
setTimeout(() => {
//处理返回结果,看是promise对象还是直接数值
resolvePromise(x,resolve,reject)
})
} else if(this.status === REJECTED) {
failCallback(this.reason)
} else {
//走到了pending分支
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
})
function resolvePromise (x,resolve,reject) {
//判断返回的是否是对象
if( x instanceof MyPromise) {
// x.then(value=>{resolve(value)},reason =>{reject(reason)})
x.then(reject,resolve)
} else {
resolve(x)
}
}
检错能力
通过在构造执行器的时候添加try,catch可以对成功和失败检错,我们封装一个检错的添加到所有的catch执行中去
01 执行器错误
02 执行函数错误
03 缓存执行函数时错误
catch方法
就是执行failcallback函数,对正确函数传参传入undefined,并不注册成功回调,而是注册失败回调
catch (failCallback) {
return this.then(undefined,failCallback)
}
All方法
解决异步并发问题,实则就是有一个数组来存,如果是数值,直接存进去,如果是promise对象,那就需要单独执行完后,将结果放入
static all (array) {
let reasult =[];
let index = 0;
return new MyPromise ((resolve,reject) => {
function addData(key,value){
reasult[key]=value
index++
if(index === array.length) {
resolve(reasult)
}
}
for(let i = 0;i<array.length;i++) {
let current = array[i]
if(current instanceof MyPromise) {
//promise对象的处理
current.then(value=>addData(i,value),reason=>reject(reason))
} else {
//对于普通值
addData(i,array[i])
}
}
})
}
resolve方法
返回结果是一个promise对象,如果是数值,需要作为参数返回一个promise对象,如果是promise对象,就直接返回结果
static resolve (value) {
if (value instanceof MyPromise) return value
return new MyPromise (resolve => resolve(value))
}
finally方法
无论是正确还是错误都会去执行结果,并且会等待延迟的执行之后再去调用
finally (callback) {
return this.then (value => {
return MyPromise.resolve(callback()).then(() => value)
},
reason => {
return MyPromise.resolve(callback()).then(() => {throw reason})
})
}
简易版的手写promise就到这里实现的差不多了,具体细节也没过多展示,下来也得多琢磨琢磨看,大家有问题可以一起谈论看看