手写Promise不完全指北

177 阅读7分钟

image.png

先实现一个最简易的Promise类

class MyPromise {
    constructor(executor) {
        this.state = 'padding'//有三种状态pending、fulfilled、rejected
        this.value = null
        // executor 是一个执行器,进入会立即执行
        // 并传入resolve和reject方法
        executor(this.resolve.bind(this), this.reject.bind(this))
    }
    // 将状态变成成功
    resolve(val) {
        this.state = 'fulfilled'
        this.value = val
    }
    // 将状态变成失败
    reject(val) {
        this.state = 'rejected'
        this.value = val
    }
}

简单测试一波

const p1 = new MyPromise((resolve, reject) => {
    resolve('success')
})
console.log(p1) // MyPromise { state: 'fulfilled', value: '成功' }

const p2 = new MyPromise((resolve, reject) => {
    reject('fail')
})
console.log(p2) // MyPromise { state: 'rejected', value: 'fail' }

Promise状态变更后不能再修改

上面没啥问题,再看下面

const p3 = new MyPromise((resolve, reject) => {
    resolve('success')
    reject('fail')
})
console.log(p3) // MyPromise { state: 'rejected', value: 'fail' }

状态经过resolve、reject之后是依据最后一个为准,我们应该让Promise状态变更后不能再修改:

    // 将状态变成成功
    resolve(val) {
        if (this.state !== 'padding') return
        this.state = 'fulfilled'
        this.value = val
    }
    // 将状态变成失败
    reject(val) {
        if (this.state !== 'padding') return
        this.state = 'rejected'
        this.value = val
    }

再次测试,完美通过

const p3 = new MyPromise((resolve, reject) => {
    resolve('success')
    reject('fail')
})
console.log(p3) // MyPromise { state: 'fulfilled', value: 'success' }

捕获executor函数错误,并且rejected

我们知道用户传入的executor可能会运行报错,抛出错误的时候我们需要把状态修改为rejected

const p4 = new MyPromise(() => {
    throw ('fail')
})
console.log(p4) // MyPromise { state: 'rejected', value: 'fail' }

那么我们try catch包装一下

        try {
            executor(this.resolve.bind(this), this.reject.bind(this))
        } catch (error) {
            this.reject(error)
        }

实现then方法

    // 传入两个参数:成功回调、失败回调,并且设置默认值
    then(onFulfilled = val => val, onRejected = val => { throw val }) {
        if (this.state === 'fulfilled') {
            onFulfilled(this.value)
        } else if (this.state === 'rejected') {
            onRejected(this.value)
        }
    }

简单测试,完美通过

const p5 = new MyPromise((resolve, reject) => {
   resolve('success')
}).then(res => console.log(res))//success

then支持在异步调用resolve或者reject后执行

以下暂不支持

const p6 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('success')
    });
}).then(res => console.log(res))//没有打印

那么我们应该如何支持呢?在then函数调用的时候还没有执行过resolve,此时的状态为padding。我们先把回调函数存起来,然后在改变promise状态的处理函数里面调用存起来的函数即可。

实现如下:

    then(onFulfilled = val => val, onRejected = val => { throw val }) {
        if (this.state === 'fulfilled') {
            onFulfilled(this.value)
        } else if (this.state === 'rejected') {
            onRejected(this.value)
            // 此时的状态为padding
        } else {
            this.onFulfilled = onFulfilled
            this.onRejected = onRejected
        }
    }
        // 将状态变成成功
    resolve(val) {
        if (this.state !== 'padding') return
        this.state = 'fulfilled'
        this.value = val
        // 如果有存起来的成功回调,那就执行
        this.onFulfilled && this.onFulfilled(val)

    }
    // 将状态变成失败
    reject(val) {
        if (this.state !== 'padding') return
        this.state = 'rejected'
        this.value = val
        // 如果有存起来的失败回调,那就执行
        this.onRejected && this.onRejected(val)
    }

执行示例,完美通过

const p7 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('success')
    });
}).then(res => console.log(res))//success

支持多个then回调

但是会有以下问题:

const p8 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve()
    });
})
p8.then(res => console.log(111))
p8.then(res => console.log(222))
p8.then(res => console.log(333))//333

我们看到只打印了333,前面的111、222都没有打印。其实是回调函数最终只指向了最后一个333的回调函数,那么我们可以用一个数组将回调函数存起来。 说干就干:

class MyPromise {
    constructor(executor) {
        this.state = 'padding'//有三种状态pending、fulfilled、rejected
        this.value = null
        this.onFulfilledLists = []
        this.onRejectedLists = []
        // executor 是一个执行器,进入会立即执行
        // 并传入resolve和reject方法
        try {
            executor(this.resolve.bind(this), this.reject.bind(this))
        } catch (error) {
            this.reject(error)
        }
    }
    // 将状态变成成功
    resolve(val) {
        if (this.state !== 'padding') return
        this.state = 'fulfilled'
        this.value = val
        // 如果有存起来的成功回调,那就执行
        while (this.onFulfilledLists.length) {
            // 依据先进先出原则,截取第一个回调执行
            this.onFulfilledLists.shift().call(this, val)
        }
    }
    // 将状态变成失败
    reject(val) {
        if (this.state !== 'padding') return
        this.state = 'rejected'
        this.value = val
        // 如果有存起来的失败回调,那就执行
        while (this.onRejectedLists.length) {
            // 依据先进先出原则,截取第一个回调执行
            this.onRejectedLists.shift().call(this, val)
        }
    }
    // 传入两个参数:成功回调、失败回调,并且设置默认值
    then(onFulfilled = val => val, onRejected = val => { throw val }) {
        if (this.state === 'fulfilled') {
            onFulfilled(this.value)
        } else if (this.state === 'rejected') {
            onRejected(this.value)
            // 此时的状态为padding
        } else {
            this.onFulfilledLists.push(onFulfilled)
            this.onRejectedLists.push(onRejected)
        }
    }
}

123全部打印出来

const p9 = new MyPromise((resolve, reject) => {
   setTimeout(() => {
       resolve()
   });
})
p9.then(res => console.log(111))//111
p9.then(res => console.log(222))//222
p9.then(res => console.log(333))//333

then支持链式调用

显然,then返回的也是一个promise

  then(onFulfilled = val => val, onRejected = val => { throw val }) {
        return new MyPromise((resolve, reject) => {
            // 为了方便,我们创建一个统一处理的函数
            const middleFunc = fn => {
                //利用 try catch捕获错误
                try {
                    const result = fn.call(this, this.value)
                    // 如果then返回的是一个promise那就继续链式调用
                    if (result && result instanceof MyPromise) {
                        result.then(resolve, reject)
                        // 如果什么也没返回或者返回一个非Promise,那就包装成promise
                    } else {
                        resolve(result)
                    }
                } catch (error) {
                    reject(error)
                }
            }
            if (this.state === 'fulfilled') {
                middleFunc(onFulfilled)
            } else if (this.state === 'rejected') {
                middleFunc(onRejected)
                // 此时的状态为padding
            } else {
                this.onFulfilledLists.push(middleFunc.bind(this, onFulfilled))
                this.onRejectedLists.push(middleFunc.bind(this, onRejected))
            }
        })
    }

完美通过测试:

const p10 = new MyPromise((resolve, reject) => {
   setTimeout(() => {
       resolve()
   });
}).then(res => console.log(111))//111
   .then(res => console.log(222))//222
   .then(res => console.log(333))//333

异步执行promise

new MyPromise(resolve => {
   resolve(111)
}).then(res => console.log(res))
console.log(222)
//打印结果
//111
//222

我们对then继续改造

  then(onFulfilled = val => val, onRejected = val => { throw val }) {
        return new MyPromise((resolve, reject) => {
            // 为了方便,我们创建一个统一处理的函数
            const middleFunc = fn => {
                // 创建一个微任务
                queueMicrotask(() => {
                    //利用 try catch捕获错误
                    try {
                        const result = fn.call(this, this.value)
                        // 如果then返回的是一个promise那就继续链式调用
                        if (result && result instanceof MyPromise) {
                            resolve.then(resolve, reject)
                            // 如果什么也没返回或者返回一个非Promise,那就包装成promise
                        } else {
                            resolve(result)
                        }
                    } catch (error) {
                        reject(error)
                    }
                })
            }
            if (this.state === 'fulfilled') {
                middleFunc(onFulfilled)
            } else if (this.state === 'rejected') {
                middleFunc(onRejected)
                // 此时的状态为padding
            } else {
                this.onFulfilledLists.push(middleFunc.bind(this, onFulfilled))
                this.onRejectedLists.push(middleFunc.bind(this, onRejected))
            }
        })
    }

再次验证:

new MyPromise(resolve => {
   resolve(111)
}).then(res => console.log(res))
console.log(222)
//打印结果
//222
//111

不能重复调用自己

使用原生 Promise 执行这个代码,会报类型错误

const p1 = new Promise((resolve, reject) => {
 resolve(100)
}).then(value => {
 console.log(value)
 return p1
})

//打印
//100
//Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

我们对then方法进行改造:

    then(onFulfilled = val => val, onRejected = val => { throw val }) {
        const p = new MyPromise((resolve, reject) => {
            // 为了方便,我们创建一个统一处理的函数
            const middleFunc = fn => {
                // 创建一个微任务
                queueMicrotask(() => {
                    //利用 try catch捕获错误
                    try {
                        const result = fn.call(this, this.value)
                        if (p === result) {
                            throw new TypeError('Chaining cycle detected for promise #<Promise>')
                        }
                        // 如果then返回的是一个promise那就继续链式调用
                        if (result && result instanceof MyPromise) {
                            result.then(resolve, reject)
                            // 如果什么也没返回或者返回一个非Promise,那就包装成promise
                        } else {
                            resolve(result)
                        }
                    } catch (error) {
                        reject(error)
                        // 创建一个宏任务继续往上抛错
                        setTimeout(() => {
                            throw error
                        })
                    }
                })
            }
            if (this.state === 'fulfilled') {
                middleFunc(onFulfilled)
            } else if (this.state === 'rejected') {
                middleFunc(onRejected)
                // 此时的状态为padding
            } else {
                this.onFulfilledLists.push(middleFunc.bind(this, onFulfilled))
                this.onRejectedLists.push(middleFunc.bind(this, onRejected))
            }
        })
        return p
    }

实现catch方法

   catch(onRejected) {
       return this.then(null, onRejected)
   }

测试一下:

new MyPromise(() => {
   throw 'fail'
}).catch(console.log) //fail

实现静态resolve和reject方法

    static resolve(data) {
        return new MyPromise(resolve => {
            resolve(data)
        })
    }
    static reject(data) {
        return new MyPromise((resolve, reject) => {
            reject(data)
        })
    }

测试一下:

MyPromise.resolve('successs') //MyPromise {state: "fulfilled", value: "successs", onFulfilledLists: Array(0), onRejectedLists: Array(0)}
MyPromise.reject('fail') //MyPromise {state: "rejected", value: "fail", onFulfilledLists: Array(0), onRejectedLists: Array(0)}

但是会有以下问题:

MyPromise.resolve(MyPromise.reject('fail')) //MyPromise {state: "fulfilled", value: MyPromise, onFulfilledLists: Array(0), onRejectedLists: Array(0)}

展开以后我们发现value是一个状态为rejected的promise

image.png 这显然不符合我们的预期:

image.png 下面我们开始对静态resolve方法传入的是个promise进行改造:

    static resolve(data) {
        // 如果传入的是个promise那就直接返回
        if (data instanceof MyPromise) {
            return data
        }
        return new MyPromise(resolve => {
            resolve(data)
        })
    }

测试通过:

MyPromise.resolve(MyPromise.reject('fail')) //MyPromise {state: "rejected", value: "fail", onFulfilledLists: Array(0), onRejectedLists: Array(0)}

完整代码


class MyPromise {
    constructor(executor) {
        this.state = 'padding'//有三种状态pending、fulfilled、rejected
        this.value = null
        this.onFulfilledLists = []
        this.onRejectedLists = []
        // executor 是一个执行器,进入会立即执行
        // 并传入resolve和reject方法
        try {
            executor(this.resolve.bind(this), this.reject.bind(this))
        } catch (error) {
            this.reject(error)
        }
    }
    // 将状态变成成功
    resolve(val) {
        if (this.state !== 'padding') return
        this.state = 'fulfilled'
        this.value = val
        // 如果有存起来的成功回调,那就执行
        while (this.onFulfilledLists.length) {
            // 依据先进先出原则,截取第一个回调执行
            this.onFulfilledLists.shift().call(this, val)
        }
    }
    // 将状态变成失败
    reject(val) {
        if (this.state !== 'padding') return
        this.state = 'rejected'
        this.value = val
        // 如果有存起来的失败回调,那就执行
        while (this.onRejectedLists.length) {
            // 依据先进先出原则,截取第一个回调执行
            this.onRejectedLists.shift().call(this, val)
        }
    }
    // 传入两个参数:成功回调、失败回调,并且设置默认值
    then(onFulfilled = val => val, onRejected = val => { throw val }) {
        const p = new MyPromise((resolve, reject) => {
            // 为了方便,我们创建一个统一处理的函数
            const middleFunc = fn => {
                // 创建一个微任务
                queueMicrotask(() => {
                    //利用 try catch捕获错误
                    try {
                        const result = fn.call(this, this.value)
                        if (p === result) {
                            throw new TypeError('Chaining cycle detected for promise #<Promise>')
                        }
                        // 如果then返回的是一个promise那就继续链式调用
                        if (result && result instanceof MyPromise) {
                            result.then(resolve, reject)
                            // 如果什么也没返回或者返回一个非Promise,那就包装成promise
                        } else {
                            resolve(result)
                        }
                    } catch (error) {
                        reject(error)
                        // 创建一个宏任务继续往上抛错
                        setTimeout(() => {
                            throw error
                        })
                    }
                })
            }
            if (this.state === 'fulfilled') {
                middleFunc(onFulfilled)
            } else if (this.state === 'rejected') {
                middleFunc(onRejected)
                // 此时的状态为padding
            } else {
                this.onFulfilledLists.push(middleFunc.bind(this, onFulfilled))
                this.onRejectedLists.push(middleFunc.bind(this, onRejected))
            }
        })
        return p
    }
    catch(onRejected) {
        return this.then(null, onRejected)
    }
    static resolve(data) {
        // 如果传入的是个promise那就直接返回
        if (data instanceof MyPromise) {
            return data
        }
        return new MyPromise(resolve => {
            resolve(data)
        })
    }
    static reject(data) {
        return new MyPromise((resolve, reject) => {
            reject(data)
        })
    }
}

疑问

最后给大家留个疑问,为什么静态reject方法不跟着resolve一起改造?

MyPromise.reject(MyPromise.resolve('success')) //打印的是什么?