浅谈Promise A+规范

308 阅读7分钟

相信promise这个单词大家都不陌生,就是诺言、承诺的意思。当一个人说承诺做一件事的时候,那么就会有三种状态,要么成功,要么失败,要么就是还在兑现诺言这个过程中。

因此,最基本的promise写法中,就有会实例化一个promise,且有一个基本的then方法,then方法中传入两个回调函数,一个返回成功,一个返回失败。在promise中,默认的状态是等待态(pending),一旦成功(resolve()),就不会失败(reject()),一旦失败,也不会成功。代码如下:

let p = new Promise(function(){
    resolve('成功');   //调用成功态  成功
}
p.then(function(data){  //成功的回调
    console.log(data);
},function(err){        //失败的回调
    console.log(err);   
})

promise基本实现

成功和失败

要实现上面代码中的功能,也是promise最基本的功能。首先,需要创建一个构造函数promise,在使用的时候传入了一个执行器executor,executor会传入两个参数:成功(resolve)和失败(reject)。之前说过,只要成功,就不会失败,只要失败就不会成功。所以,默认状态下,在调用成功时,就返回成功态,调用失败时,返回失败态。代码如下:

let promise = function Promise(executor){
    let self = this;   //保存this
    self.status = 'pending';  //默认状态是pending,既不成功也不失败
    self.value = undefind;    //成功态的默认值
    self.reason = undefind;   //失败态的默认值
    //一会解释为何定义这两个数组
    self.resolvedArr = [];
    self.rejectedArr = [];
    //成功方法
    function resolve(value){         
        if(self.status ==='pending'){
            self.status = 'resolved';
            self.value = value;
            //一会解释这句代码的意义
            self.resolvedArr.forEach(function(fn){
                fn();
            })
        }
    }
    //失败方法
    function reject(reason){         
        if(self.status === 'pending'){
            self.status = 'rejected';
            self.reason = 'reason';
            //一会解释这句代码的意义
            self.rejectedArr.forEach(function(fn){
                fn();
            })
        }
    }
    executor(resolve,reject);
}

promise A+规范规定,在有异常错误时,则执行失败函数。

则上方代码应改为:

let promise = function Promise(executor){
    ......
    try{
        executor(resolve,reject);
    }catch(e){
        reject(e);
    }
}

then方法

then方法是promise的最基本的方法,返回的是两个回调,一个成功的回调,一个失败的回调,实现过程如下:

promise.prototype.then = function(onFulfilled,onRejected){
    let self = this;
    if(self.status==='resolved'){  //成功状态时的调用
        onFulfilled(self.value);
    }
    if(self.status==='rejected'){  //失败状态时的调用
        onRejected(self.reason);
    }
}

Promise的高阶用法

多次调用then

在promise规范中,规定可以多次调用then,返回多次结果。

使用resolve()来举例:

let p = new Promise(function(){
    resolve('已经成功啦');
})
p.then(function(data){console.log(data);},function(err){});
p.then(function(data){console.log(data);},function(err){});
p.then(function(data){console.log(data);},function(err){});

返回的结果

已经成功啦
已经成功啦
已经成功啦

为了实现这样的效果,则上一次的代码将要重新写过,我们可以把每次调用resolve的结果存入一个数组中,每次调用reject的结果存入一个数组。这就是为何会在上面定义两个数组,且分别在resolve()和reject()遍历两个数组的原因。因此,在调用resolve()或者reject()之前,我们在pending状态时,会把多次then中的结果存入数组中,则上面的代码会改变为:

promise.prototype.then = function(onFulfilled,onRejected){
    let self = this;
    if(self.status==='resolved'){  //成功状态时的调用
        onFulfilled(self.value);
    }
    if(self.status==='rejected'){  //失败状态时的调用
        onRejected(self.reason);
    }
    //依次将resolve()和reject()的结果分别存入数组
    if(self.status==='pending'){
        self.resolvedArr.push(function(){  //将返回的每个成功的结果存入数组
            onFulfilled(self.value);
        })
        self.rejectedArr.push(function(){  //将返回的每个失败的结果存入数组
            onRejected(self.reason);
        })
    }
}

后面为节省时间我将只举例resolve()中代码的变化,reject()与resolve()的分析是一样的。

链式调用

Promise A+规范中规定then方法可以链式调用

在promise中,要实现链式调用返回的结果是返回一个新的promise,第一次then中返回的结果,无论是成功或失败,都将返回到下一次then中的成功态中,但在第一次then中如果抛出异常错误,则将返回到下一次then中的失败态中

链式调用失败时

举例如下:

let p = new Promise(function(){
    reject('失败');
})
p.then(function(data){
    
},function(err){
    console.log(err);
    return '我是返回值';
}).then(function(data){
    console.log(data);   //我是返回值
},function(){
        
})

因此要实现链式调用,则需要定义返回的结果为一个新的promise,只要是promise,就会有两个状态,成功态和失败态,根据之前代码,实例化一个promise,传入resolve和reject参数。则上面的代码将被改变为:

promise.prototype.then = function(onFulfilled,onRejected){
    let self = this;
    let promise2;  //表示返回新的promise
    if(self.status==='resolved'){  //成功状态时的调用
        pormise2 = new Promise(function(resolve,reject){
            try{
                onFulfilled(self.value);
            }catch(e){
                reject(e);
            }
            
        })
    }
    if(self.status==='rejected'){  //失败状态时的调用
        promise2 = new Promise(function(resolve,reject){
            try{
                onRejected(self.reason);
            }catch(e){
                reject(e);
            }
        })
    }
    //依次将resolve()和reject()的结果分别存入数组
    if(self.status==='pending'){
        self.resolvedArr.push(function(){
            promise2 = new Promise(function(resolve,reject){
                try{
                    onFulfilled(self.value);
                }catch(e){
                    reject(e);
                }
            })
        })
        self.rejectedArr.push(function(){
            promise2 = new Promise(function(resolve,reject){
                try{
                    onRejected(self.reason);
                }catch(e){
                    reject(e);
                }
            })
        })
    }
    return promise2;
}

链式调用成功时

链式调用成功会返回值,有多种情况,根据举的例子,大致列出可能会发生的结果。因此将链式调用返回的值单独写一个方法。方法中传入四个参数,分别是p2,x,resolve,reject,p2指的是上一次返回的promise,x表示运行promise返回的结果,resolve和reject是p2的方法。则代码写为:

function resolvePromise(p2,x,resolve,reject){
    ....
}
  • 返回结果不能是自己

举例如下:

var p = new Promise(function(resovle,reject){
    return p;     //返回的结果不能是自己,
})

当返回结果是自己时,永远也不会成功或失败,因此当返回自己时,应抛出一个错误

function resolvePromise(p2,x,resolve,reject){
    if(px===x){
        return reject(new TypeError('循环引用'));
    }
    ....
}
  • 返回结果可能是promise

当是promise时,有可能是别人写的promise,promise可能是一个对象,或者函数,考虑各种可能。若是别人写的promise,里面的then,别人可能直接赋予一个常量,如:then:1,但then是一个函数,因此需要判断,当then满足是函数时,则调用then方法。如果是一个普通值(then:1),则直接返回(resolve())。 代码如下

function resolvePromise(p2, x, resolve, reject) {
    // 看x是不是一个promise,如果是,则promise应该是一个对象
    
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {//当x为promise时
        // 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
        try { // {then:1}
            let then = x.then;
            if (typeof then === 'function') {
                // 这里的then可能是全局上的then,因此,需让它定位是属于x的then,则用call
                then.call(x, function (y) {
                    if (called) return
                    called = true
                    // y可能还是一个promise,在去解析直到返回的是一个普通值
                    resolvePromise(promise2, y, resolve, reject)
                }, function (err) { //失败
                    reject(err);
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            reject(e);
        }
    } else { // 说明是一个普通值1
        resolve(x); // 表示成功了
    }
}

promise.prototype.then = function(onFulfilled,onRejected){
    let self = this;
    let promise2;  //表示返回新的promise
    if(self.status==='resolved'){  //成功状态时的调用
        pormise2 = new Promise(function(resolve,reject){
            try{
                let x = onFulfilled(self.value);   //将返回的结果赋值给x,x有可能是普通值,也有可能是promise
                //在resolvePromise中传入四个参数,第一个是返回的promise,第二个是返回的结果,第三个和第四个分别是resolve()和reject()的方法。
                resolvePromise(promise2,x,resolve,reject);
            }catch(e){
                reject(e);
            }
        })
    }
    ......
}

如果返回的结果是一个普通的值,

  • 返回结果可能为一个普通值,则直接

resolve(x);

  • Promise一次只能调用成功或者失败

也就是当调用成功就不能再调用失败了,如果两个都调用的时候,哪个先调用就执行哪一个。

function resolvePromise(p2,x,resolve,reject()){
    let called; // 表示是否调用过成功或者失败
    if(x !== null && (typeof x === 'object' || typeof x === 'function'){
        try{
            let then = x.then;
            if (typeof then === 'function') {
                then.call(x, function(y){
                    ......
                },function(err){
                    //预防同时调用成功或失败
                    if (called) return;
                    called = true;
                    ......
                })
            }
        }catch(e){
            //预防同时调用成功或失败
            if (called) return
            called = true;
            ......
        }
    }else{
        ......
    }
}

个人认为,这个地方比较绕,需要慢慢的一步一步的理清楚。

Promise A+规范的其他规定

promise中值的穿透

then链式调用多次空且最后一次传值时 p.then().then().then(function(){},function(){})

Promise.prototype.then = function(onFulfilled,onRejected){
    onFulfilled = typeof onFulfilled === 'function'?onFulfilled:function(value){
        return value;
    }
    onRejected = typeof onRjected === 'function'?onRejected:function(err){
        throw err;
    }
    
    ......
}

根据promise A+规范原理,promise在自己的框架中,封装了一系列的内置的方法。

  • 捕获错误的方法 Promise.prototype.catch()
  • 解析全部方法 Promise.all()
  • 竞赛 Promise.race()
  • 生成一个成功的promise Promise.resolve()
  • 生成一个失败的promise Promise.reject()

最后调用自己写的框架时,需加上这一句代码 module.exports = Promise;

关于这篇promise A+规范的总结,肯定会存在很多不足的地方,欢迎各位提出宝贵的意见或建议,也希望能帮助到你从中获得一些知识!