【手撕系列】Promise then的实现(二)

89 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情

then 方法基础功能实现

上一节我们简单实现了 resolve和reject 对结果的处理,但它的结果是直接输出的,我们希望它能够和们熟悉的用法一样,通过then方法来接受处理结果的回调,现在我们来一起实现一个简单的 then 方法

class MyPromise {
    // ...
​
    then(onFulfilled, onRejected) {
        if (this.state === MyPromise.fulfilled) {
            onFulfilled(this.result)
        }
        if (this.state === MyPromise.rejected) {
            onRejected(this.error)
        }
    }
}
​
new MyPromise((resolve, reject) => {
    resolve(1)
    reject(2)
}).then(res => {
    console.log(res);
}, error => {
    console.log(error)
})

这里,我们通过 onFulfilled,onRejected 接收我们 then 里的回调函数,通过判断 state 状态来决定走哪个分支,这样一个简单的 then 方法就完成了,但它是一个同步的方法,大家知道 then 最大的作用是异步的处理结果,那我们接下来就对 then 做异步的处理

then 方法异步实现

当传进 MyPromise 的回调函数是一个异步方法的时候,我们就无法保证调用 then 时能够拿到我们想要的结果(异步事件还未处理完),那这个时候 Promise 的状体仍旧处于 Pending 状态,那我们这里可以采用发布订阅者模式,在调用 then 方法过程中,如果状态是 pending 的,先把要执行的函数记录下来,后续状态改变时在把记录的方法执行就可以了

在实现代码之前先简单介绍下发布订阅者模式的实现

发布订阅者的实现思路

  • 维护一个缓存列表(事件调度中心)
  • 订阅者把函数 fn 添加到缓存列表中(订阅者注册事件到调度中心)
  • 发布者在事件发生时通知调度中心(发布者发布事件到调度中心,调度中心处理代码)

按照上述思路我们简单梳理下逻辑

  • 事件调度中心

这个场景下,我们需要记录的事件就是 resolve与reject 事件,他们两个是相互独立的,那我们就定义两个数组onResolveCallbacks 和 onRejectCallbacks去作为我们的事件调度中心

// 事件调度中心
this.onResolveCallbacks = []
this.onRejectCallbacks = []
  • 订阅者

then 方法则作为订阅者,当执行时如果状态还为 pending 时,把回调方法添加到数组中(事件调度中心)

// 等待状态时暂存回调方法(这里的方法注意用箭头函数包裹起来)
if (this.state === MyPromise.pending) {
    this.onResolveCallbacks.push(() => {
        onFulfilled(this.result)
    })
    this.onRejectCallbacks.push(() => {
        onRejected(this.error)
    })
}
  • 发布者

最后当resolve或者reject执行时,作为发布者把事件调度中心注册的事件进行执行

const resolve = (res) => {
    if (this.state === MyPromise.pending) {
        // ...
        
        // 状态改变发布消息时执行对应调度中心的方法
        this.onResolveCallbacks.forEach(callBack => callBack())
    }
}

这样,一个简单的订阅发布者模式就完成了,完整代码

class MyPromise {
    static pending = 'pending'
    static fulfilled = 'fulfilled'
    static rejected = 'rejected'
​
    constructor (callBack) {
        this.state = MyPromise.pending
        this.result = null
        this.error = null
        this.onResolveCallbacks = []
        this.onRejectCallbacks = []
​
        const resolve = (res) => {
            if (this.state === MyPromise.pending) {
                console.log(this.state + ' => ' + MyPromise.fulfilled)
                this.state = MyPromise.fulfilled
                this.result = res
                this.onResolveCallbacks.forEach(callBack => callBack())
            }
        }
        const reject = (error) => {
            if (this.state === MyPromise.pending) {
                console.log(this.state + ' => ' + MyPromise.rejected)
                this.state = MyPromise.rejected
                this.error = error
                this.onRejectCallbacks.forEach(callBack => callBack())
            }
        }
​
        callBack(resolve, reject)
​
        // console.log('result:' + this.result)
        // console.log('error:' + this.error)
    }
​
    then(onFulfilled, onRejected) {
        // Both onFulfilled and onRejected are function
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (v) => v
        onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw err }
​
        if (this.state === MyPromise.fulfilled) {
            onFulfilled(this.result)
        }
        if (this.state === MyPromise.rejected) {
            onRejected(this.error)
        }
        // 等待状态时暂存回调方法
        if (this.state === MyPromise.pending) {
            this.onResolveCallbacks.push(() => {
                onFulfilled(this.result)
            })
            this.onRejectCallbacks.push(() => {
                onRejected(this.error)
            })
        }
    }
}
​

我们来试一下执行一下异步方法

new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    }, 1000)
}).then(res => {
    console.log(res);
}, error => {
    console.log(error)
})

顺利打印出来没有问题了

2022-10-23 15 17 06

好了,本篇已经带大家实现了promise中then的基础功能,但是还不够,我们知道,原生 Promise 的 then 方法是可以链式调用的,也就是可以一直 .then().then()... 下去,下一节我会跟大家一起实现 promise 的 then 的链式调用,敬请关注!