• 如何实现一个promise


    如何实现一个Promise

    1 实现Promise构造函数

    Promise自身包含一个状态属性PromiseStaus,一个异步结果属性PromiseResult
    状态只在pending、fulfilled、rejected之间枚举,改变状态的两个函数resolve、reject由Promise自身的执行器函数提供(根据A+规范这个executor执行器函数必传否则抛出异常),而Promise的结果由用户提供,且状态一旦从pending更改为其他便不可逆

    1.1 关于executor

    executor函数在Promise构造函数的执行就立即同步执行。在这个过程中,用户可以调用resolve和reject改变promise实例的状态
    状态的改变可以是异步的,比如在定时器的回调中修改,或者在一个网络请求返回后修改,当然也可能是同步的,即直接同步修改
    执行过程中一旦抛出异常,Promise内部便会捕获它,同时调用reject将其处理

    1.2 Promise构造函数及其作用

    Promise构造函数的作用:

    1. 生成Promise实例
    2. 向外暴露改变Promise实例状态的方法
    3. 捕获执行器函数抛出的异常,使用reject将其处理
    4. 根据1.1,如果状态的修改是异步的,那么需要将then中的回调函数存储在promise实例中,一旦状态改变就立即调用(后续then的实现中会说明)
    const PromiseStatusMap = {
        pending: 'pending',
        fulfilled: 'fulfilled',
        rejected: 'rejected'
    }
    
    const isPendingStatus = status => status === PromiseStatusMap.pending
    const isFunction = target => typeof target === 'function'
    const doAsyncTask = callback => {
        setTimeout(() => {
            callback()
        }, 0)
    }
    
    function Promise(executor) {
        if (typeof executor !== 'function') {
            throw 'the first parameter must be a function.'
        }
    
        this.PromiseResult = null
        this.PromiseStatus = PromiseStatusMap.pending
        this.callbacks = []
    
        const onFulfilled = (value) => {
            // 状态一但改变不可再次更改
            if (!isPendingStatus(this.PromiseStatus)) {
                return
            }
            this.PromiseResult = value
            this.PromiseStatus = PromiseStatusMap.fulfilled
            doAsyncTask(() => {
                this.callbacks.forEach(handleCallback => {
                    handleCallback()
                })
            })
        }
    
        const onRejected = (reason) => {
            // 状态一但改变不可再次更改
            if (!isPendingStatus(this.PromiseStatus)) {
                return
            }
    
            this.PromiseResult = reason
            this.PromiseStatus = PromiseStatusMap.rejected
            doAsyncTask(() => {
                this.callbacks.forEach(handleCallback => {
                    handleCallback()
                })
            })
        }
    
        try {
            // 改变状态的方法由promise提供,方法的参数由用户提供给promise处理
            executor(onFulfilled, onRejected)
        } catch (error) {
            // 一旦抛出异常,状态变为失败
            console.warn(error)
            onRejected(error)
        }
    }
    

    2 实现then

    2.1 关于then

    then是promise原型上的方法,它将Promise成功或者失败的回调提供给用户,同时将promise的结果注入到回调函数的形参中,让用户能在promise得到结果之后去处理接下来的事情

    在promise中,then中的回调是异步执行的

    2.2 then到底做了什么事情

    在promise中,then要做什么事情,取决于promise的状态。根据1.1可知,then回调执行时,promise的状态可能已经改变,也可能尚未改变

    1. 如果状态的改变是同步的,即then执行的时候promise的状态不为pending,那么then需要根据promise的状态分别执行成功或失败的回调,并将promise结果值注入到回调的形参中去
    2. 如果状态的修改是异步的,即then执行的时候promise的状态为pending,那么then需要将用户提供的回调存储起来,等待promise的状态改变的时候再执行对应的回调

    简而言之,then就做了两件事情。一是在promise拿到结果之后执行对应的成功或失败的回调,二是对外返回一个新的promise

    2.3 then的返回值

    then一定返回一个新的promise,这个新的promise根据成功或失败回调的返回值callbackResult决定

    1. 如果用户没有定义成功/失败的回调,那么给一个默认的function,实现then链式传递或异步中断的效果

    2. 如果callbackResult的返回值是Promise类型,那么then的返回值状态和结果与callbackResult保持一致

    3. 如果callbackResult的返回值不是Promise类型,则返回一个成功的Promise

    4. 如果抛出异常,则调用reject,返回一个失败的promise

    2.4 实现then

    根据以上,为promise的原型添加then方法

    Promise.prototype.then = function (onFulfilled, onRejected) {
    
        return new Promise((resolve, reject) => {
    
            // 2.3.1
            !isFunction(onFulfilled) && (onFulfilled = () => { })
            !isFunction(onRejected) && (onRejected = (reason) => { throw reason })
    
            // 根据promise状态去执行成功/失败的回调,并且注入相应的promise结果
            const handleCallback = () => {
                try {
                    const fn = this.PromiseStatus === PromiseStatusMap.rejected ? onRejected : onFulfilled
                    const callbackResult = fn(this.PromiseResult)
                    if (callbackResult instanceof Promise) {
                        callbackResult.then(v => {
                            resolve(v)
                        }, r => {
                            reject(r)
                        })
                    } else {
                        resolve(callbackResult)
                    }
                } catch (error) {
                    console.warn(error)
                    reject(error)
                }
            }
    
            // 2.2.1  如果then执行的时候状态已经改变
            if (this.PromiseStatus !== PromiseStatusMap.pending) {
                // 2.1 回调的执行是异步的
                doAsyncTask(handleCallback)
            } else {
                // pending状态
                // 2.2.2
                this.callbacks.push(handleCallback)
            }
    
        })
    }
    

    3 实现静态方法resolve与reject

    Promise.resolve返回一个成功的promise
    Promise.reject返回一个失败的promise
    如果参数是一个promise,那么采用这个promise的状态与结果

    Promise.resolve = function(result){
        return new Promise((resolve,reject)=>{
            if(result instanceof Promise){
                result.then((value)=>{
                    resolve(value)
                },(reason)=>{
                    console.log('here')
                    reject(reason)
                })
            }else{
                resolve(result)
            }
        })
    }
    
    Promise.reject = function(result){
        return new Promise((resolve,reject)=>{
            if(result instanceof Promise){
                result.then(value=>{
                    resolve(value)
                },reason=>{
                    reject(reason)
                })
            }else{
                reject(result)
            }
        })
    }
    

    4 实现catch方法

    执行回调的过程中,异常已经被handleCallback中的trycatch捕获并reject处理,因此只需要传递一个失败的回调给then即可

    Promise.prototype.catch = function(reason){
        return new Promise((undefined,reject)=>{
            reject(reason)
        })
    }
    

    5 实现静态方法all

    all用于并行执行异步操作,接受一个可迭代的promises对象作为参数,返回一个新的Promise
    只有全部异步操作都成功时成功,有一个异步操作失败就失败
    如果成功,则返回的成功promise与传入的promises顺序保持一致

    需要注意:

    1. 输入判断。接受的参数是一个可迭代的类型,并不一定是数组,因此需要先判断入参是否可迭代,并且使用forof迭代entries属性以便同时拿到索引和元素
    2. 为了保证输入与输出的顺序一致,我们需要根据索引去赋值PromiseResult数组中的元素。但是在判定所有promise都得到结果的时候,我们不应该用.length属性做判断,因为js数组的长度是可以被随意更改指定的,而且我们不确定哪个promise会先完成,也就是说,我们数组长度和完成的异步任务的数量并不一定对等,因此需要创建一个递增的变量count来作这件事情。
    3. 异常捕获,一旦抛出异常就调用reject将其处理
    Promise.all = function (promises) {
        if (typeof promises?.[Symbol.iterator] !== 'function') {
            throw 'the first parameter must be iterable(need a Symbol.iterator property)'
        }
        return new Promise((resolve, reject) => {
            try {
                const result = []
                // 根据索引赋值,不能用result.length做判断,因为我们不确定哪个promise会先完成
                // arr[2] = 1  我们可能会返回:arr:[empty,empty,1]
                let count = 0
                for (const [index, promiseItem] of promises.entries()) {
    
                    if (promiseItem instanceof Promise) {
                        promiseItem.then(value => {
                            result[index] = value
                            ++count === promises.length && resolve(result)
                        }, reason => {
                            reject(reason)
                        })
                    } else {
                        result[index] = promiseItem
                        ++count === promises.length && resolve(result)
                    }
                }
            } catch (error) {
                console.warn(error)
                reject(error)
            }
        })
    
    }
    

    5.1 实现静态方法allSettled

    对于all,所有异步任务成功才成功,有一个失败就返回失败,而有时候我们的场景可能是这样的:即使某些任务失败了,也将所有异步任务的结果返回。这就是allSettled的用途

    在all的基础上实现allSettled其实非常容易,我们只需将"一旦失败就返回一个失败的promise"这个逻辑改成"不论成功或失败,所有异步任务得到结果才返回一个新的Promise"即可

    const judgeResolve = ()=> ++count === promises.length && resolve(result)
    if (promiseItem instanceof Promise) {
        promiseItem.then(value => {
            result[index] = { status: 'fulfilled', value }
            judgeResolve()
        }, reason => {
            result[index] = { status: 'rejected', reason }
            judgeResolve()
        })
    } else {
        result[index] = { status: 'fulfilled', value: promiseItem }
        judgeResolve()
    }
    

    6 实现静态方法race

    和all一样,race方法接受一个可迭代的数据作为形参,返回一个新的promise,返回的promise值采用第一个得到结果的promise的result值

    Promise.race = function (promises) {
        if (typeof promises?.[Symbol.iterator] !== 'function') {
            throw 'the first parameter must be iterable(need a Symbol.iterator property)'
        }
        return new Promise((resolve, reject) => {
            try {
                for (const promiseItem of promises) {
                    if (promiseItem instanceof Promise) {
                        promiseItem.then((value) => {
                            resolve(value)
                        }, (reasom) => {
                            reject(reasom)
                        })
                    } else {
                        resolve(promiseItem)
                    }
                }
            } catch (error) {
                console.warn(error)
                reject(error)
            }
        })
    }
    
    

    7 实现finally方法

    Promise.prototype.finally = function(fn){
        return new Promise((resolve,reject)=>{
            this.then((value)=>{
                resolve(value)
            },(reason)=>{
                reject(reason)
            })
        })
    }
    

    8 代码汇总

    const PromiseStatusMap = {
        pending: 'pending',
        fulfilled: 'fulfilled',
        rejected: 'rejected'
    }
    
    const isPendingStatus = status => status === PromiseStatusMap.pending
    const isFunction = target => typeof target === 'function'
    const doAsyncTask = callback => {
        setTimeout(() => {
            callback()
        }, 0)
    }
    
    function Promise(executor) {
        if (typeof executor !== 'function') {
            throw 'the first parameter must be a function.'
        }
    
        this.PromiseResult = undefined
        this.PromiseStatus = PromiseStatusMap.pending
        this.callbacks = []
    
        const onFulfilled = (value) => {
            // 状态一但改变不可再次更改
            if (!isPendingStatus(this.PromiseStatus)) {
                return
            }
            this.PromiseResult = value
            this.PromiseStatus = PromiseStatusMap.fulfilled
            doAsyncTask(() => {
                this.callbacks.forEach(handleCallback => {
                    handleCallback()
                })
            })
        }
    
        const onRejected = (reason) => {
            // 状态一但改变不可再次更改
            if (!isPendingStatus(this.PromiseStatus)) {
                return
            }
    
            this.PromiseResult = reason
            this.PromiseStatus = PromiseStatusMap.rejected
            doAsyncTask(() => {
                this.callbacks.forEach(handleCallback => {
                    handleCallback()
                })
            })
        }
    
        try {
            // 改变状态的方法由promise提供,方法的参数由用户提供给promise处理
            executor(onFulfilled, onRejected)
        } catch (error) {
            // 一旦抛出异常,状态变为失败
            console.warn(error)
            onRejected(error)
        }
    }
    
    Promise.prototype.then = function (onFulfilled, onRejected) {
    
        return new Promise((resolve, reject) => {
    
            // 2.3.1
            !isFunction(onFulfilled) && (onFulfilled = () => { })
            !isFunction(onRejected) && (onRejected = (reason) => { throw reason })
    
            // 根据promise状态去执行成功/失败的回调,并且注入相应的promise结果
            const handleCallback = () => {
                try {
                    const fn = this.PromiseStatus === PromiseStatusMap.rejected ? onRejected : onFulfilled
                    const callbackResult = fn(this.PromiseResult)
                    if (callbackResult instanceof Promise) {
                        callbackResult.then(v => {
                            resolve(v)
                        }, r => {
                            reject(r)
                        })
                    } else {
                        resolve(callbackResult)
                    }
                } catch (error) {
                    console.warn(error)
                    reject(error)
                }
            }
    
            // 2.2.1  如果then执行的时候状态已经改变
            if (this.PromiseStatus !== PromiseStatusMap.pending) {
                // 2.1 回调的执行是异步的
                doAsyncTask(handleCallback)
            } else {
                // pending状态
                // 2.2.2
                this.callbacks.push(handleCallback)
            }
    
        })
    }
    
    Promise.resolve = function (result) {
        return new Promise((resolve, reject) => {
            if (result instanceof Promise) {
                result.then((value) => {
                    resolve(value)
                }, (reason) => {
                    reject(reason)
                })
            } else {
                resolve(result)
            }
        })
    }
    
    Promise.reject = function (result) {
        return new Promise((resolve, reject) => {
            if (result instanceof Promise) {
                result.then(value => {
                    resolve(value)
                }, reason => {
                    reject(reason)
                })
            } else {
                reject(result)
            }
        })
    }
    
    Promise.prototype.catch = function (reason) {
        return new Promise((undefined, reject) => {
            reject(reason)
        })
    }
    
    Promise.all = function (promises) {
        if (typeof promises?.[Symbol.iterator] !== 'function') {
            throw 'the first parameter must be iterable(need a Symbol.iterator property)'
        }
        return new Promise((resolve, reject) => {
            try {
                const result = []
                // 根据索引赋值,不能用result.length做判断,因为我们不确定哪个promise会先完成
                // arr[2] = 1  我们可能会返回:arr:[empty,empty,1]
                let count = 0
                for (const [index, promiseItem] of promises.entries()) {
    
                    if (promiseItem instanceof Promise) {
                        promiseItem.then(value => {
                            result[index] = value
                            ++count === promises.length && resolve(result)
                        }, reason => {
                            reject(reason)
                        })
                    } else {
                        result[index] = promiseItem
                        ++count === promises.length && resolve(result)
                    }
                }
            } catch (error) {
                console.warn(error)
                reject(error)
            }
        })
    
    }
    
    Promise.race = function (promises) {
        if (typeof promises?.[Symbol.iterator] !== 'function') {
            throw 'the first parameter must be iterable(need a Symbol.iterator property)'
        }
        return new Promise((resolve, reject) => {
            try {
                for (const promiseItem of promises) {
                    if (promiseItem instanceof Promise) {
                        promiseItem.then((value) => {
                            resolve(value)
                        }, (reasom) => {
                            reject(reasom)
                        })
                    } else {
                        resolve(promiseItem)
                    }
                }
            } catch (error) {
                console.warn(error)
                reject(error)
            }
        })
    }
    
    Promise.prototype.finally = function(fn){
        return new Promise((resolve,reject)=>{
            this.then((value)=>{
                resolve(value)
            },(reason)=>{
                reject(reason)
            })
        })
    }
    
    module.exports = {
        Promise
    }
    
    
  • 相关阅读:
    kubernetes概述
    pygame--图形模块
    iPhone 上你可能还不知道的小技巧
    使用 JdbcTemplate 查询数据时报错:列名无效(已解决)
    【分享】怎样做架构师?
    关于 oh-my-zsh 插件的使用(以 Sublime Text 为例)
    VI/VIM 无法使用系统剪贴板(clipboard)?(Ubuntu&Mac OS X已解决)
    Ubuntu 下 Sublime 无法输入中文?(已解决)
    VIM 的帮助文档在哪里?看这里。
    推荐一款好用的文件/文件夹对比工具 —— Beyond Compare
  • 原文地址:https://www.cnblogs.com/ltfxy/p/16131536.html
Copyright © 2020-2023  润新知