JavaScript 怎么自己手写一个Promise

认真看完这篇文章, 您可以自己封装一个简易但功能相对齐全的Promise, 还可以加深对Promise的理解

建议 : 看这篇文章之前希望您


文章较长, 代码连贯性较强, 从简单开始入手, 读者可以按需选读


一. 最简单的Promise

class Promise {
    constructor (executor) {
        if (typeof executor !== 'function') 
            throw new TypeError(`Promise resolver ${executor} is not a function`) 
        
        /* 默认状态 */
        this.state = 'pending'
        this.value = undefined
        this.reason = undefined
        
        /* 
            状态函数 resolve, reject
            1.pending -> fulfilled, pending -> rejected 
            2.把数据储存到Promise实例上 this.value = value, this.reason = reason
        */
        const resolve = value => {
            if (this.state === 'pending') {
                this.state = 'fulfilled'
                this.value = value
            }
        }
        const reject = reason => {
            if (this.state === 'pending') {
                this.state = 'rejected'
                this.reason = reason
            }
        }
        
        executor(resolve, reject)
    }
    
    then (onFulfilled, onRejected) {
        if (this.state === 'fulfilled') {
            onFulfilled(this.value)
        }
        if (this.state === 'rejected') {
            onRejected(this.reason)
        }
    }
}
复制代码

ps : 测试工具为vsCode的Quokka插件

根据Promise的状态函数resrej,对应执行then中的处理函数onFulfilledonRejected

二. 异步的Promise

1. then()为异步

我们都知道,Promise中的then函数的代码是异步执行的,而我们写的这个并不是,可以验证一下

显然这段代码是同步执行的,而我们想要的输出顺序是 0 2 1,所以我们可以使用setTimeout模拟这个异步

class Promise {
    constructor (executor) { ... }
    
    then (onFulfilled, onRejected) {
        if (this.state === 'fulfilled') {
        
            /* 使用setTimeout模拟异步 */
            setTimeout(() => {
                onFulfilled(this.value)
            }, 0);
            
        }
        if (this.state === 'rejected') {
            setTimeout(() => {
                onRejected(this.reason)
            }, 0);
        }
    }
}
复制代码

ok, 完美得到我们想要的!

2. 状态函数异步执行

当状态函数 res/rej为异步执行时, 我们可以看到 then是没有反应的 左边灰色小方块表明这行代码没有执行

为什么呢? 那是因为当执行到then函数的时候,res为异步执行,所以状态还是pending,而我们的then函数里面还没有对状态为pending的处理, 修改一下代码

class Promise {
    constructor (executor) { 
        ...
        /* 状态函数异步执行时, 处理函数的存储列表 */
        this.resolveCallBackList = []
        this.rejectCallBackList = []
    
        const resolve = value => {
            if (this.state === 'pending') {
                ...
                /* 如果有, 则执行处理函数列表里的函数 */
                this.resolveCallBackList.length > 0 
                && this.resolveCallBackList.forEach(e => e())
            }
        }
        const reject = reason => {
            if (this.state === 'pending') {
                ...
                this.rejectCallBackList.length > 0 
                && this.rejectCallBackList.forEach(e => e())
            }
        }
        ...
    }
    
    then (onFulfilled, onRejected) {
        ...
        
        /* 状态为pending时, 把处理函数存储对相应的列表 */
        if (this.state === 'pending') {
            onFulfilled && this.resolveCallBackList.push( () => {
                onFulfilled(this.value)
            })
            onRejected && this.rejectCallBackList.push( () => {
                onRejected(this.reason)
            })
        }
    }
}
复制代码

这样, 状态函数异步执行的时候也可以处理了, 可以简单理解为, 当状态为pending时, 把处理函数onFulfilled/onRejected存起来, 等状态函数res/rej执行时, 自动执行对应的处理函数

三. Promise的错误捕捉

当发生错误时, Promise不会报错, 而是由失败的处理函数then函数的第二个函数捕捉错误并处理, 如果我们自己写的Promise发生错误的话, 毫无意外是直接报错的, 就像这样

既然执行时发生错误, 那么我们就可以使用try/catch去捕获错误

class Promise {
    constructor (executor) {
        ...
        
        /* 使用try/catch捕获错误, 并执行reject, 改变状态为rejected */
        try {
            executor(resolve, reject)
        } catch (error) {
            this.state === 'pending' && reject(error)
        }
    }
    
    then (onFulfilled, onRejected) { ... }
}
复制代码

四. then函数详解

then函数有两个特性

  • then函数执行完返回一个新的Promise实例
  • then函数能链式调用

1. then的链式调用

new Promise(res => res(0))
.then(value => {
    console.log(value)   // 0
    return `1 fulfilled`
})
.then(value => {
    console.log(value)   // 1 fulfilled
})
复制代码

then函数执行后返回一个Promise实例, 该Promise实例的状态由then决定, 下一个then函数根据返回的这个Promise实例执行相应的处理函数, 画个图

下一个 then的执行依赖于上一个 then执行返回的 Promise实例, 而这个 Promise实例的数据由上一个 then的处理函数 onFulfilled/onRejected执行和其返回值决定

2.then的处理函数返回值不是一个Promise实例

如果按照字面意思去写代码

class Promise {
    constructor (executor) { ... }
    
    then (onFulfilled, onRejected) {
        /* 一个新的Promise实例 */
        const newPromise = new Promise ( (res, rej) => {})
        
        ...
        
        return newPromise
    }
}
复制代码

如果这样写, 是没意义的, 返回的Promise实例的状态永远为pending, 因为没有执行状态函数res/rej, 因此也无法进行then函数的链式调用

因为new Promise(executor)executor函数是同步执行的, 所以我们可以这样写

class Promise {
    constructor (executor) { ... }
    
    then (onFulfilled, onRejected) {
        const newPromise = new Promise ( (res, rej) => {
        
            /* 
                这部分的处理函数是同步执行的, 因此可以放在里面执行 
                同时还能通过res/rej改变返回的Promise实例的状态 
            */
            if (this.state === 'fulfilled') {
                setTimeout(() => {
                
                    /* 拿到处理函数执行后的返回值 */
                    const value = onFulfilled(this.value)
                    /* 改变返回的Promise实例的状态并把数据传过去 */
                    res(value)
                
                    
                }, 0);
            }
            if (this.state === 'rejected') {
                setTimeout(() => {
                    const reason = onRejected(this.reason)
                    res(reason)
                }, 0);
            }
    
            if (this.state === 'pending') {
                onFulfilled && this.resolveCallBackList.push( () => {
                    const value = onFulfilled(this.value)
                    res(value)
                })
                onRejected && this.rejectCallBackList.push( () => {
                    const reason = onRejected(this.reason)
                    res(reason)
                })
            }
        })
        
        return newPromise
    }
}
复制代码

哒哒, then的链式调用完成了

ps : then的处理函数返回值不是一个Promise实例时, 无论fullfilled还是rejected, 都是执行下一个then函数的onFulfilled

3.then的处理函数返回值是一个Promise实例

then的处理函数返回值是一个Promise实例时, 则下一个then函数的执行, 全部由这个Promise实例决定, 所以我们需要使用checkReturnValueIfPromise函数去判断一下返回值的类型并处理对应的情况

class Promise {
    constructor (executor) { ... }
    
    /* 
        promise -> Promise对象 
        target -> then的处理函数的返回值  
        res/rej -> 要返回的Promise实例的状态函数
    */
    checkReturnValueIfPromise (promise, target, res, rej) {
        if (target instanceof promise) {
        
            /* 
                如果是Promise实例
                则调用then函数,根据Promise实例的状态执行对应的处理函数
                从而改变要返回的Promise实例的状态
                如果下面的代码不能理解, 也可以写成这样
                    target.then( value => {
                        res(value)
                    }, reason => {
                        rej(reason)
                    } )
            */
            target.then(res, rej)
        
            
        } else {
            res(target)
        }
    }
    
    then (onFulfilled, onRejected) {
        const newPromise = new Promise ( (res, rej) => {
            if (this.state === 'fulfilled') {
                setTimeout(() => {
                
                    const value = onFulfilled(this.value)
                    /* 调用检测函数并做相关处理 */
                    this.checkReturnValueIfPromise(Promise, value, res, rej)
                    
                }, 0);
            }
            if (this.state === 'rejected') {
                setTimeout(() => {
                    const reason = onRejected(this.reason)
                    this.checkReturnValueIfPromise(Promise, reason, res, rej)
                }, 0);
            }
    
            if (this.state === 'pending') {
                onFulfilled && this.resolveCallBackList.push( () => {
                    const value = onFulfilled(this.value)
                    this.checkReturnValueIfPromise(Promise, value, res, rej)
                })
                onRejected && this.rejectCallBackList.push( () => {
                    const reason = onRejected(this.reason)
                    this.checkReturnValueIfPromise(Promise, reason, res, rej)
                })
            }
        })
        
        return newPromise
    }
}
复制代码

就算是异步也是一点毛病都没有

五. 一些Promise上的方法 (直接上代码)

对了, 还有一个与then类似的方法catch, 这个方法是专门处理rejected状态的, 代码也就只有一句话

class Promise {
    constructor () { ... }
    
    then () { ... }
    
    catch (onRejected) {
        this.then(undefined, onRejected)
    }
}
复制代码

1. Promise.resolve

返回一个fulfilled状态的Promise实例

class Promise {
    constructor () { ... }
    
    then () { ... }
    
    catch () { ... }
    
    static resolve (value) {
        return new Promise( res => res(value))
    }
}
复制代码

2. Promise.reject

返回一个rejected状态的Promise实例

class Promise {
    constructor () { ... }
    
    then () { ... }
    
    catch () { ... }
    
    static resolve () { ... }
    
    static reject (reason) {
        return new Promise( (undefined, rej) => rej(reason))
    }
}
复制代码

3. Promise.race

接收一个Promise实例的数组promiseArray, 返回一个Promise实例, 返回的Promise实例由promiseArray中执行最快的Promise实例决定

class Promise {
    constructor () { ... }
    
    then () { ... }
    
    catch () { ... }
    
    static resolve () { ... }
    
    static reject () { ... }
    
    static race (promiseArray) {
        return new Promise ( (res, rej) => {
            promiseArray.forEach( promise => {
                promise.then(res, rej)
            })
        }) 
    }
}
复制代码

4. Promise.all

功能描述太长了, 不懂的可以去看 阮一峰老师对于Promise.all的介绍

class Promise {
    constructor () { ... }
    
    then () { ... }
    
    catch () { ... }
    
    static resolve () { ... }
    
    static reject () { ... }
    
    static race () { ... }
    
    static all (promiseArray) {
        let count = 0,
            resultArray = []
        
        return new Promise( (res, rej) => {
            promiseArray.forEach( promise => {
                promise.then( value => {
                    count++
                    resultArray.push(value)
                    if (count === promiseArray.length) {
                        res(resultArray)
                    }
                }, reason => {
                    rej(reason)
                })
            })
        })
    }
}
复制代码

六. 完整的代码 (带注解)

上面的代码也不是完美的, 还有一些细枝末节的问题没解决, 不过也完成了核心的功能, 下面给出稍微完整的代码(带注解)

class Promise {
    constructor (executor) {
        if (typeof executor !== 'function') {
            throw new TypeError(`Promise resolver ${executor} must be a function`)
        }

        this.state = 'pending'
        this.value = undefined
        this.reason = undefined
        /* 异步时, then()函数的执行函数(onFulfilled, onRejected)的存储列表 */
        this.resolveCallBackList = []
        this.rejectCallBackList = []

        /**
         * @method resolve
         * @param {string} value 成功时的参数
         * @function 改变状态, 传递参数, 遍历成功异步缓存函数列表
         * @returns {undefined}
         */
        const resolve = value => {
            if (this.state === 'pending') {
                this.state = 'fulfilled'
                this.value = value
                this.resolveCallBackList.length > 0 && this.resolveCallBackList.forEach(e => e())
            }
        }

        /**
         * @method reject
         * @param {string} reason 失败时的参数
         * @function 改变状态, 传递参数, 遍历失败异步缓存函数列表
         * @returns {undefined}
         */
        const reject = reason => {
            if (this.state === 'pending') {
                this.state = 'rejected'
                this.reason = reason
                this.rejectCallBackList.length > 0 && this.rejectCallBackList.forEach(e => e())
            }
        }

        /* 实例 Promise过程发生错误, 则直接把状态改为 rejected */
        try {
            executor(resolve, reject)
        } catch (error) {
            this.state === 'pending' && reject(error)
        }
    }

    /**
     * @method checkLastThenReturn
     * @param {Promise} promise Promise对象
     * @param {*} target then处理函数(onFulfilled/onRejected)的返回值
     * @param {function} res 下一个Promise实例的成功状态转换函数
     * @param {function} rej 下一个Promise实例的失败状态转换函数
     * @function 判断then()的处理函数的返回值是否为新的Promise实例
     */
    checkLastThenReturn (promise, target, res, rej) {
        if (target instanceof promise) {
            /* 如果target是一个新的 Promise实例,则根据该实例的执行,改变下一个Promise实例的状态 */
            target.then(res, rej)
        } else {
            /* 如果不是, 则直接执行成功的状态改变函数 */
            res(target)
        }
    }

    /**
     * @method then
     * @param {function} onFulfilled 状态为fulfilled时执行
     * @param {function} onRejected 状态为rejected时执行
     * @function 返回一个 Promise实例, 该Promise实例的状态,由该 then()的执行情况决定
     * @returns {Promise} 返回一个新的Promise对象
     */
    then (onFulfilled, onRejected) {
        /* 处理 then()不传参数且上一个操作为异步时, 直接返回上一个实例 */
        if (!onFulfilled && !onRejected && this.state === 'pending') return this

        /* 处理 then(onFulfilled)只有成功处理函数但状态为 rejected的情况, 直接返回一个失败的Promise实例 */
        if (!onRejected && this.state === 'rejected') return Promise.reject(this.reason)

        /* 处理 then()不传参的问题,直接把处理函数重置为返回接收的参数 */
        if (!onFulfilled && !onRejected) {
            onFulfilled = value => value
            onRejected = reason => reason
        }

        /**
         * @method returnPromise
         * @param {function} res 使返回的Promise对象状态转为 fulfilled
         * @param {function} rej 使返回的Promise对象状态转为 rejected
         * @function 同步执行,根据then()的执行情况,执行res还是 rej,改变该Promise实例的状态并执行异步存储函数列表
         */
        const returnPromise = new Promise( (res, rej) => {
            /* then()的处理函数同步执行 */
            if (this.state === 'fulfilled') {
                /* 使用setTimeout模拟 then()里面的内容异步执行 */
                setTimeout(() => {
                    /* 如果处理函数出错,则返回的 Promise实例为 rejected */
                    try {
                        const value = onFulfilled(this.value)
                        this.checkLastThenReturn(Promise, value, res, rej)
                    } catch (error) {
                        rej(error)
                    }
                }, 0);
            }
            if (this.state === 'rejected') {
                setTimeout(() => {
                    try {
                        const reason = onRejected(this.reason)
                        this.checkLastThenReturn(Promise, reason, res, rej)
                    } catch (error) {
                        rej(error)
                    }
                }, 0);
            }

            /* then()处理函数异步执行 */
            if (this.state === 'pending') {
                onFulfilled && this.resolveCallBackList.push( () => {
                    try {
                        const value = onFulfilled(this.value)
                        this.checkLastThenReturn(Promise, value, res, rej)
                    } catch (error) {
                        rej(error)
                    }
                })

                if (onRejected) {
                    this.rejectCallBackList.push( () => {
                        try {
                            const reason = onRejected(this.reason)
                            this.checkLastThenReturn(Promise, reason, res, rej)
                        } catch (error) {
                            rej(error)
                        }
                    })
                } else {
                    /* 异步时没有onRejected, 直接把下一个Promise对象变为失败状态 */
                    this.rejectCallBackList.push( () => {
                        // Promise.reject(this.reason).catch(rej)
                        rej(this.reason)
                    })
                }
            }
        })

        return returnPromise
    }

    /**
     * @method catch
     * @param {function} onRejected 失败时的处理函数
     * @function 没有onFulfilled的 then()
     * @returns {Promise}
     */
    catch (onRejected) {
        this.then(undefined, onRejected)
    }

    /**
     * @method reject
     * @param {string} reason 
     * @function 返回一个失败状态的Promise实例
     * @returns {new Promise}
     */
    static reject (reason) {
        return new Promise( (undefined, rej) => rej(reason))
    }

    /**
     * @method resolve
     * @param {string} value 
     * @function 返回一个成功状态的Promise实例
     * @returns {new Promise}
     */
    static resolve (value) {
        return new Promise( res => res(value))
    }

    /**
     * @method race
     * @param {array} promiseArray Promise实例的数组
     * @function 找出Promise实例数组中执行最快的 Promise实例
     * @returns 返回一个Promise实例,该实例的状态由 Promise实例数组中执行最快的一个 Promise实例决定
     */
    static race (promiseArray) {
        return new Promise ( (res, rej) => {
            promiseArray.forEach( promise => {
                promise.then(res, rej)
            })
        }) 
    }
    
    /**
     * @method all
     * @param {array} promiseArray Promise实例的数组 
     * @function 等Promise实例数组的每个 Promise实例都执行完再把所有执行的结果放进一个数组返回,如果出错,则终止并返回出错那个Promise的数据
     * @returns 返回一个 Promise实例
     */
    static all (promiseArray) {
        let count = 0,
            resultArray = []
        
        return new Promise( (res, rej) => {
            promiseArray.forEach( promise => {
                promise.then( value => {
                    count++
                    resultArray.push(value)
                    if (count === promiseArray.length) {
                        res(resultArray)
                    }
                }, reason => {
                    rej(reason)
                })
            })
        })
    }
}
复制代码

七. 结语

谢谢浏览我的文章, 希望你能学到东西

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值