模拟实现Promise

569 阅读3分钟

Promise实现原理

我们都知道,new一个Promise时,需要传递一个函数exectF,函数一般以两个修改状态函数作为参数,第一个函数是来将pending状态修改成成功状态,第二个函数是来将pending状态修改成失败状态,只有状态发送变化,promise的then函数才会执行。创建实例时,将promise的resolve和reject传给exectF,立即执行该函数,函数内部若执行了参数方法,则会执行对应的resolve方法或reject方法,从而修改promise的状态,

class _Promise{
    constructor(exectF){
        this.status='pending'//初始状态
        this.value=undefined//传递给then函数的参数
        this.resolveQueue=[]
        this.rejectQueue=[]
        //判断是否传入了一个函数,否则执行会出错
        typeof exectF==='function'&&
        exectF(resolve.bind(this),reject.bind(this))
        
        function resolve(result){
            //模拟promise的异步调用then方法
            if(this.status==='pending'){
                this.status='fulfilled'
                this.value=result 
                this.resolveQueue.forEach((fn)=>{
                    fn()
                })
            }  
        }
        
        function reject(result){
            if(this.status==='pending'){
                this.status='rejected'
                this.value=result 
                this.rejectQueue.forEach((fn)=>{
                    fn()
                })            
            }
        }
    }
    
    /*因为js是从上到下执行的,then函数属于异步任务(在此处采用setTimeout模拟),
    所以若遇到then函数时,该promise对象状态为pending,将当前的then操作存入队列,状态变化后再执行
    (比如执行函数存在定时器,且在定时器回调函数执行resolve或reject函数,定时器属于异步代码,
    遇到then方法时,resolve或reject方法还未执行,只有定时器的回调函数执行后promise对象的状态才会变化)
    */
        
    then(fn1,fn2){
        var result,pre=this
        if(this.status==='pending'){
            /*因为then返回新promise对象,且影响新对象的状态,
            所以在执行then操作时,更改下一个promise对象的状态
            */
            return new _Promise((resolve,reject)=>{
                /*状态变化时,执行当前对象状态对应的队列中的回调函数,
                并更改下一个promise对象的状态
                */
                pre.resolveQueue.push(()=>{
                    try{
                        result=fn1(pre.value)
                        /*如果回调函数返回一个promise对象,
                        则将promise的返回值传给下一个promise
                        */
                        if(result instanceof _Promise){
                            result.then(resolve,reject)
                        }
                        else resolve(result)
                    }catch(e){
                        reject(e)
                    }
                })
                pre.rejectQueue.push(()=>{
                    try{
                        result=fn2(pre.value)
                        if(result instanceof _Promise){
                            result.then(resolve,reject)
                        }
                        else resolve(result)
                    }catch(e){
                        reject(e)
                    }
                })
            })
        }
        
        /*当状态为成功或失败时,无论是fulfill还是rejected状态的执行函数,
        若无设置返回promise对象,则默认返回成功状态的promise对象,
        传入参数为执行函数的返回值
        */
        else{
            return new _Promise((resolve,reject)=>{
                //模拟异步调用
                setTimeout(()=>{
                    try{
                        if(pre.status==='fulfilled'){
                            result=fn1(pre.value)
                        }
                        else result=fn2(pre.value)
                        if(result instanceof _Promise){
                            result.then(resolve,reject)
                        }
                        else resolve(result)
                    }catch(e){
                        reject(e)
                    }
                })
            })
        }
    }
    
    catch(fn){
        return this.then(undefined,fn)
    }
    
    finally(fn){
        return this.then((resolve)=>{
            _Promise.resolve(fn()).then(()=>resolve)
        },(reject)=>{
            _Promise.resolve(fn()).then(()=>{throw reject})
        })
    }
    
}

_Promise.resolve=function(result){
    return new _Promise(resolve=>resolve(result))
}

_Promise.reject=function(result){
    return new _Promise((undefined,reject)=>reject(result))
}


使用Promise简单实现Promise.all

Promise.all()将多个 Promise 实例包装成一个新的 Promise 实例。新的Promise的状态由所有promise实例状态决定,只有所有的promise的状态都变成fulfilled,新的promise的状态才会变成fulfilled。而只要有一个promise是rejected,新promise的状态就变成rejected

//前提条件是输入数组,且元素均为promise实例
function all(arr){
    var values=[],n=arr.length
    return new Promise((resolve,reject)=>{
        
        arr.forEach((promise,index)=>{
            promise.then((value)=>{
                values.push(value)
                if(index===n-1){
                    resolve(values)
                }
            },(e)=>reject(e))
        })
    })
}

/*优化,使用Promise.resolve,因为Promise.all()
方法接受一个数组作为参数,数组元素都是 Promise 实例,
如果不是,就会先调用Promise.resolve方法,将参数转为 Promise实例。
*/
function all(arr){
    var values=[],n=arr.length
    return new Promise((resolve,reject)=>{
        
        arr.forEach((promise,index)=>{
            Promise.resolve(promise).then((value)=>{
                values.push(value)
                if(index===n-1){
                    resolve(values)
                }
            },(e)=>reject(e))
        })
    })
}
使用Promise实现Promise.race

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。只要所有promise实例中有一个实例先改变状态,就会改变新promise的状态。那个率先改变的 Promise 实例的返回值,就传递给新promise的回调函数。Promise.race()方法的参数与Promise.all()方法一样,如果不是Promise实例,就会先调用Promise.resolve()方法,将参数转为 Promise 实例

function race(arr){
    var n=arr.length
    return new Promise((resolve,reject)=>{
        arr.forEach((promise,index)=>{
            Promise.resolve(promise).then((value)=>{
                resolve(value)
            },(e)=>reject(e))
        })
    })
}

之前就已经手动实现过,今天整理发现有许多遗漏的细节,所以又花了点时间完善,测试过多组数据都可以,不过可能还有缺陷,所以如果有问题,欢迎指出~还有一些Promise方法还没实现,之后会一一补上

参考 1.《ECMAScript 6 入门教程》阮一峰 2.blog.csdn.net/weixin_4473…