前端开发核心知识进阶 2.6 你以为我真的想让你手写Promise吗

通过前面儿篇的学习,我们认识到,想优雅地进行异步操作,必须要熟识-个极其重要的质概念Promise。 它是取代传统回调,实现同步链式写法的解决方案;是理解Generator、async/awat的关键。但是,对于初学者来说,Promise 并不是很好理解,其中的概念纷杂,且抽象程度较高。与此同时,在中高级前端开发面试中,对于Promise 的考查也多种多样,近几年流行“让面试者实现一个Promise” 。那么,本篇就带大家实现一个简单的Promise。

1.1 Promise的雏形

function Cpromise(executor){
    this.status = 'pending'
    this.value = ''
    this.reason = ''

    const resolve = value => {
            if(this.status === 'pending'){
                this.value = value
                this.status = 'fulfilled'
            }
    }

    const reject = reason => {
            if(this.status === 'pending'){
                this.status = 'rejected'
                this.reason = reason
            }

    }
    executor(resolve,reject)
}

Cpromise.prototype.then = function(onFulfilled,onReject){
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : data => data
    onReject = typeof onReject === 'function' ? onReject : error => { throw error }
    if(this.status === 'fulfilled'){
        onFulfilled(this.value)
    }

    if(this.status === 'rejected'){
        onReject(this.reason)
    }
}

解析:这就是promise的构架,现在还没首先一部功能,promise接受一个函数参数,在函数参数里传人promise对象声明的resolve和reject函数,然后执行函数体。每次实例一个pormise对象,都会声明状态status,成功结果value,失败结果reason。在resolve中把状态变化fulfilled,在reject中把状态变为rejected。promise的then函数接受俩个函数参数,给俩个函数设置默认值,实现promise穿透功能。状态为fulfilled执行第一参数函数,状态为rejected执行第二参数函数。

1.2 实现异步

function Cpromise(executor){
    this.status = 'pending'
    this.value = ''
    this.reason = ''
    this.onFulfilledFncArr = []
    this.onRejectFncArr = []

    const resolve = value => {
        if(value instanceof Cpromise){
            value.then(resolve,reject)
        }
        setTimeout(() => {
            if(this.status === 'pending'){
                this.value = value
                this.status = 'fulfilled'
    
                this.onFulfilledFncArr.forEach( fn => {
                    fn(value)
                })
            }
        })
    }

    const reject = reason => {
        setTimeout(() => {
            if(this.status === 'pending'){
                this.status = 'rejected'
                this.reason = reason
    
                this.onRejectFncArr.forEach( fn => {
                    fn(reason)
                }) 
            }
        })

    }
    executor(resolve,reject)
}

Cpromise.prototype.then = function(onFulfilled,onReject){
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : data => data
    onReject = typeof onReject === 'function' ? onReject : error => { throw error }
    if(this.status === 'fulfilled'){
        onFulfilled(this.value)
    }

    if(this.status === 'rejected'){
        onReject(this.reason)
    }
    if(this.status === 'pending'){
        this.onFulfilledFncArr.push(onFulfilled)
        this.onRejectFncArr.push(onReject)
    }
}

解析:在relsolve和rejects中加入setTimeout模拟微任务,在resolve中识别参数为prmise,则直接调用then函数。声明onFulfilledFncArr和onRejectFncArr数组存储多个then在promise决议时该调用的函数,在resolve调用时执行onFulfilledFnc里所有函数,给每个函数传入value。reject调用时,时执行onRejectFncArr里所有函数,给每个函数传入reason。

例:多个then的调用

let p = new Cpromise((res,rej) =>{
    setTimeout(() => {
        res(5)
    },500)

})
p.then(res => {
    console.log(res + 1);
})

p.then(res => {
    console.log(res + 2);
})

1.3 实现链式调用

关于then的俩个函数参数返回值,如果返回简单数据类型,返回一个pormise实例promise2,在其reslove、reject中传入返回值。如果返回值是是promise类型,那就进行遍历,看看这个返回值promise的then的onFulfilled的传入值是不是promise,是就再进行遍历。将最后拿到的值,决议promise2

例:then返回promise

new Cpromise((res,rej) => {
    console.log('start');
    setTimeout(() =>{
        res(5)
    },1000)
    
}).then(val => {
    return new Cpromise((res,rej) => {
        setTimeout(() => {
            res(val + 5)
        },1000)
    })
}).then(val => {
    return new Cpromise((res,rej) => {
        setTimeout(() => {
            res(val + 5)
        },1000)
    })
}).then(res => {
    console.log(res);
})

代码如下:

function Cpromise(executor){
    this.status = 'pending'
    this.value = ''
    this.reason = ''
    this.onFulfilledFncArr = []
    this.onRejectFncArr = []

    const resolve = value => {
        if(value instanceof Cpromise){
            value.then(resolve,reject)
        }
        setTimeout(() => {
            if(this.status === 'pending'){
                this.value = value
                this.status = 'fulfilled'
    
                this.onFulfilledFncArr.forEach( fn => {
                    fn(value)
                })
            }
        })
    }

    const reject = reason => {
        setTimeout(() => {
            if(this.status === 'pending'){
                this.status = 'rejected'
                this.reason = reason
    
                this.onRejectFncArr.forEach( fn => {
                    fn(reason)
                }) 
            }
        })

    }
    executor(resolve,reject)
}




Cpromise.prototype.then = function(onFulfilled,onReject){
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : data => data
    onReject = typeof onReject === 'function' ? onReject : error => { throw error }
    let promise2
    if(this.status === 'fulfilled'){
        return Promise2 = new Cpromise((res,rej) => {
            setTimeout(() => {
                try {
                    let result = onFulfilled(this.value)
                    resolvePromise(promise2, result, res, rej)
                } catch (error) {
                    rej(error)
                }
            })
        })
    }

    if(this.status === 'rejected'){
        return Promise2 = new Cpromise((res,rej) => {
            setTimeout(() => {
                try {
                    let result = onReject(this.value)
                    resolvePromise(promise2, result, res, rej)
                } catch (error) {
                    rej(error)
                }
            })
        })
        
    }
    if(this.status === 'pending'){
        return Promise2 = new Cpromise((res,rej) => {
            this.onFulfilledFncArr.push(() => {
                try {
                    let result = onFulfilled(this.value)
                    resolvePromise(promise2, result, res, rej)
                } catch (error) {
                    rej(error)
                }
            }) 
            this.onRejectFncArr.push(() => {
                try {
                    let result = onReject(this.season)
                    resolvePromise(promise2, result, res, rej)
                } catch (error) {
                    rej(error)
                }
            }) 
        })
        
    }
}

function resolvePromise(promise2, result, res, rej){
    if(result === promise2){
        rej(new TypeError('error due circular reference'))
    }
    let consumed = false
    let thenFn
    if(result instanceof Cpromise){
        if(result.status === 'pending'){
            result.then((data) => {
                resolvePromise(promise2, data, res, rej)
            },rej)
        }else{
            result.then(res,rej)
        }
        return
    }
    const isComplexResult = target => (typeof target === 'function' || typeof target === 'Object') && (typeof target !== 'null')

    if(isComplexResult(result)){
        try {
            thenFn = result.then
            if(typeof thenFn === 'function'){
                thenFn.call(result,
                function(data){
                    if(consumed){
                        return
                    }
                    consumed = true

                    return resolvePromise(promise2, data, res, rej)
                },function(error){
                    if(consumed){
                        return
                    }
                    consumed = true
                    return rej(error)
                })
            }else{
                res(result)
            }
        } catch (error) {
            if(consumed){
                return
            }
            consumed = true
            return rej(error)
        }
    }else{
        res(result)
    }
}

解析:在then中返回值为promise2,在promise2中对onFulfilled、onRejected的返回值进行resolvePromise函数遍历。resolvePromise函数工具流程:检测是不是promise类型,是,检测状态是不是pending,是then传入onFulfilled函数,函数体中继续resolvePromise。不是pending,直接then,传入promise2的resolve和reject。然后return。

不是promise类型,判断其是不是复杂数据类型,是,判断有没有then,有再判断是不是函数,是则传入俩个函数,在第一个函数中继续resolvePromise遍历。没有then,then不是函数,直接执行promise2的resolve。

1.4 catch、resolve、all、race的实现

Cpromise.prototype.catch = function(catchFn){
    return new this.then(null,catchFn)
}

Cpromise.resolve = function(value){
    new Cpromise(res => {
        res(value)
    })
}

Cpromise.reject = function(reason){
    new Cpromise((res,rej) => {
        rej(reason)
    })
}

Cpromise.all = function(promiseArr){
    return new Cpromise((allRes,allRej) => {
        try {
            const valArr = []
            let onfulfilledPromiseCount = 0
            for(let i=0; i<promiseArr.length; i++){
                promiseArr[i].then(res => {
                    valArr.push(res)
                    onfulfilledPromiseCount++
                    if(onfulfilledPromiseCount === promiseArr.length){
                        allRes(valArr)
                    }
                },allRej)
            }
        } catch (error) {
            allRej(error)
        }
    })
}


Cpromise.race = function(promiseArr){
    return new Cpromise((raceRes,raceRej) => {
        try {
            for(let i=0; i<promiseArr.length; i++) {
                promiseArr[i].then(raceRes,raceRej)
            }
        } catch (error) {
            raceRej(error)
        }
    })

}




通过本篇的学习,相信读者对Promise这个概念的理解会大大加深。其实,实现一个Promise不是目的,并且实现这个Promnise也没有完全100%遵循规范,应该掌握概念,融会贯通。另外,从整体来看,这部分内容不好理解,如果暂时难以接受全部概念,也不要灰心。实现的代码就在那里,要有决心慢慢地掌握它。

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值