手写一个Promise

614 阅读4分钟

Promise是一种异步编程的解决方案,用写法看似链式的调用方式避免了异步回调过于复杂的情况,最近手写一个Promise以加深对其的理解并开拓一下思路。

  1. 准备工作
  2. 实现一个.then()
  3. 实现完整的Promise

准备工作

建立一个index.html和一个promise-extra.js,覆盖原有的Promise。最基本的,Promise有自己的状态,有then方法和catch方法,并且接收一个回调函数processor,其中可以执行异步或同步操作。

html代码

<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>手写Promise</title>
    </head>
    <body>
        <script src="./esimorp.js"></script>
        <script>
            console.log(Promise)
        </script>
    </body>
    </html>

js代码

(function(global){    
    class Promise {        
        constructor(processor){            
            this._status = 'pending'        
        }
        then(){}
        catch(){}    
    }
    global.Promise = Promise
})(window)

控制台输出

class Promise {                                                                                      index.html:11 
    constructor(processor){
        this._status = 'pending'
    }
    then(){}
    catch(){}
}

以上完成了就可以正式开始。。。

Promise中的回调函数processor应提供两个参数且这两个参数都是函数,以便用户对Promise当前的状态进行决议,当然,这个processor也不是必须的。另外Promise应该有将状态决议为fullfilled的方法和rejected的方法,我将其分别命名为_resolve和_reject,这两个函数接收的参数就是用户决议时传进来的参数。同时,then和catch都可以接收回调以便在Promise决议后进行相应的操作。于是把代码补充一下

class Promise {        
    constructor(processor){            
        this._status = 'pending'                        
        const context = this
        if(!processor){                
            return            
        }
        processor(                
            res => {                    
                context._resolve(res)                
            },                
            err => {                    
                context._reject(err)                
            }            
        )        
    }
    then(onFullfilled){            
        this.onFullfilled = onFullfilled        
    }
    catch(onRejected){            
        this.onRejected = onRejected
    }
    _resolve(res){            
        this._status = 'fullfilled'            
        this.currentValue = res        
    }
    _reject(err){            
        this._status = 'rejected'            
        this.currentErr = err        
    }    
}

实现一个.then()

先实现一个.then(),这里要考虑两个情况:

  1. processor中的操作是异步的
  2. processor中的操作是同步的
......
then(onFullfilled){            
    this.onFullfilled = onFullfilled
    //情况2同步执行,当状态为fullfilled时
    if(this._status === 'fullfilled'){
        this.onFullfilled(this.currentValue)            
    }        
}
...... 
_resolve(res){            
    this._status = 'fullfilled'            
    this.currentValue = res
    //情况1异步执行,onFullfilled先挂载,随后调用
    if(this.onFullfilled){    
        this.onFullfilled(this.currentValue)
    }        
}

这样一个.then()就搞定了,.catch()同理。

实现完整Promise

那么要能够链式传递下去,.then()就不能只是执行一下回调onFullfilled,而是应该每次执行都返回一个新的Promise,我在每个Promise对象上添加了next属性,用于存放当前Promise对象的后一个新Promise对象。此外,把onFullfilled或onRejected函数的执行代码抽离出来,进行错误捕获,并对下一个Promise进行决议。还有一个情况要注意,例如当前Promise的状态决议为rejected了,但紧跟着的不是.catch()而是.then(),那么就要把这个状态传递下去,即对next进行_reject(),直到传到.catch()为止,反之同理。

......
/** onFullfilled和onRejected的统一执行方法
* @param value 当前传递的值 this.currentValue 或 this.currentErr 
* @param processer 状态凝固之后的回调 this.onFullfilled 或 this.onRejected 
* @param next 当前Promise的next 
*/
taskCallback(value, processor, next){            
    let result = null            
    let normal = 1
    try {                
        result = processor(value)            
    } catch (err){
        normal = 0
        result = err
    }
    //如果next的状态已凝固,那么其状态不可再变
    if(next && (next._status ==='fullfilled' || next._status === 'rejected')){
        return
    }
    //如果result仍然是Promise,那么执行其then或catch方法获得其返回值交由next决议
    if(result instanceof Promise){    
        result.then(res => {
            next._resolve(res)
        })
        result.catch(err => {
            next._reject(err);
        });
        return
    }
    if(normal === 1){                
        next._resolve(result)
    } else {
        next._reject(result)
    }        
}
......
then(onFullfilled){
    this.onFullfilled = onFullfilled
    //创建一个新的Promise对象,挂到当前Promise对象的next属性上。不用传processor,因为.then()或.catch()产生的Promise不由用户手动决议
    this.next = new Promise()
    if(this._status === 'fullfilled'){
        //执行onFullfilled的代码抽离成一个统一的函数
        this.taskCallback(this.currentValue, this.onFullfilled.bind(this), this.next)    
    }
    //当前状态为rejected时,对next进行_reject(),使状态传递下去
    if(this._status === 'rejected'){                        
        this.next._reject(this.currentErr)
    }
    return this.next    //返回新的Promise        
}

另外由于第一个手动声明的Promise只会挂载onRejected或onFullfilled中的其中一个(Promise后要么跟.then(onFullfilled)要么跟.catch(onRejected),故只能挂载一个),所以在原型方法中要初始化一下这两个方法,否则在_resolve()或_reject()时不执行taskCallback,也就不会开始链式调用。

......
    onRejected(err){            
        this.next._reject(err)        
    }
    onFullfilled(res){            
        this.next._resolve(res)        
    }
......

至此,已完成了Promise。参考then方法可以用同样的方法补全catch方法。

最后附上完整代码

(function(global){
    
    class Promise {
        constructor(processor){
            this._status = 'pending'

            const context = this

            if(!processor){
                return
            }

            processor(
                res => {
                    context._resolve(res)
                },
                err => {
                    context._reject(err)
                }
            )
        }

        onRejected(err){
            this.next._reject(err)
        }

        onFullfilled(res){
            this.next._resolve(res)
        }

        taskCallback(value, processor, next){
            let result = null
            let normal = 1

            try {
                result = processor(value)
            }
            catch (err){
                normal = 0
                result = err
            }

            if(next && (next._status ==='fullfilled' || next._status === 'rejected')){
                return
            }

            if(result instanceof Promise){
                result.then(res => {
                    next._resolve(res)
                })

                result.catch(err => {
                    next._reject(err);
                });
                return
            }

            if(normal === 1){
                next._resolve(result)
            }
            else {
                next._reject(result)
            }
        }

        then(onFullfilled){
            this.onFullfilled = onFullfilled
            this.next = new Promise()

            if(this._status === 'fullfilled'){
                this.taskCallback(this.currentValue, this.onFullfilled.bind(this), this.next)
            }

            if(this._status === 'rejected'){
                this.next._reject(this.currentErr)
            }

            return this.next
        }

        catch(onRejected){
            this.onRejected = onRejected
            this.next = new Promise()
            if(this._status === 'rejected'){
                this.taskCallback(this.currentErr, this.onRejected.bind(this), this.next)
            }

            if(this._status === 'fullfilled'){
                this.next._resolve(this.currentValue)
            }

            return this.next
        }

        _resolve(res){
            this._status = 'fullfilled'
            this.currentValue = res
            if(this.next && this.onFullfilled){
                this.taskCallback(this.currentValue, this.onFullfilled.bind(this), this.next)
            }
        }

        _reject(err){
            this._status = 'rejected'
            this.currentErr = err
            if(this.next && this.onRejected){
                this.taskCallback(this.currentErr, this.onRejected.bind(this), this.next)
            }
        }
    }

    global.Promise = Promise
})(window)