从0实现一个基本的Promise类(三)

121 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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)
        }
    }

存入回调数组后,我们就需要在状态处理后去执行这些回调了。也就是在resolvereject改变状态后,依次执行数组中的每一项回调。

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方法的链式调用