Promise的实现及解析

5,595 阅读10分钟

Promise雏形(加入状态机)

function Promise(executor){

    let self = this;
    self.status = 'pending'; 
    self.value = undefined;
    self.reason = undefined;
    function resolve(value){
        if( self.status === 'pending'){
            self.status = 'fulfilled';
            self.value = value;
        }
    }
    function reject(reason){
        if( self.status === 'pending'){
            self.status = 'rejected';
            self.reason = reason;
        }
    }
    executor(resolve,reject);
}
Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this;
    if(self.status === 'fulfilled'){
        onFulfilled(self.value);
    }
    if(self.status === 'rejected'){
        onRejected(self.reason);
    }
}
module.exports = Promise;

测试用例1

let Promise = require('Mypromise');

let promise = newPromise(function(resolve,reject){
        resolve(results)
})
promise.then(function(data){
    console.log(data);
},function(err){
    console.log(err);
})

首先我们定义一个status状态变量,默认Promise处于pending状态

promise接收一个executor函数,该函数在构造promise实例时就执行,所以在Promise构造方法中执行executor,executor需要接收resolve作为执行成功时的回调函数,接收reject作为执行失败时的回调函数,所以定义了resolve和reject方法

resolve接收执行成功的返回结果作为参数
reject 接收执行失败的返回结果作为参数
所以这里定义了value表示成功结果,reason表示错误原因
调用resolve或reject后,会让promise进入filfilled成功状态或rejected失败状态,并且只有promise为在pending状态下,才能切换到成功/失败态

promise实例需要用then方法注册执行成功/失败的回调方法,then中根据promise所处状态,判断调用成功还是失败的回调
这样一个简单的promise的实现就写好了

加入异步回调处理,支持注册多个then

function Promise(executor){ 
    let self = this;
    self.status = 'pending'; 
    self.value = undefined;
    self.reason = undefined;
    self.onResolvedCallbacks = [];
    self.onRejectedCallbacks = [];
    
    function resolve(value){
        if( self.status === 'pending'){
            self.status = 'fulfilled'; 
            self.value = value; 
            self.onResolvedCallbacks.forEach(function(fn){
                fn();
            })
        }
    }
    
    function reject(reason){
        if( self.status === 'pending'){//只能从pending状态切换到rejected状态
            self.status = 'rejected';
            self.reason = reason;
            self.onRejectedCallbacks.forEach(function(fn){
                fn();
            })
        }
    }
    executor(resolve,reject);
}

Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this;
    if(self.status === 'fulfilled'){
        onFulfilled(self.value);
    }
    if(self.status === 'rejected'){
        onRejected(self.reason);
    }
   
    if(self.status === 'pending'){
        self.onResolvedCallbacks.push( function(){
            onFulfilled(self.value)
        });
        self.onRejectedCallbacks.push( function(){
            onRejected(self.reason)
        });
    }
}
module.exports = Promise;

测试用例2

let promise = new Promise(function(resolve,reject){
    http.get(url, function(results) {
        resolve(results)
    })
})
promise.then(function(data){
    console.log('data',data);
},function(err){
    console.log('err',err);
})
promise.then(function(data){
    console.log('data',data);
},function(err){
    console.log('err',err);
})
promise.then(function(data){
    console.log('data',data);
},function(err){
    console.log('err',err);
})

promise主要用于处理异步回调的情况,上例中发起http请求,请求成功后,用resolve发起成功的回调;并调用多次then注册了多个成功、失败的回调方法;如果执行成功,原生的promise会将每个then的成功回调都执行一遍

由于异步的请求,当调用then时,promise还处于pending状态,所以我们需要将then注册的回调方法暂存,以便成功或失败时回调,为此定义 onResolvedCallbacks 、onRejectedCallbacks 分别存放then注册的成功、失败回调方法,上例所示, 可能多次调用then注册,所以onResolvedCallbacks =[],是个数组
当执行成功会调用resolve,那我们在实现resolve方法时,将所有then中的成功回调都调用一遍,就是这段代码

self.onResolvedCallbacks.forEach(function(fn){
    fn();
})

executor异常处理

当执行异步操作时有可能发生异常,需要try/catch捕获到异常,并使promise进入rejected状态

try {
    executor(resolve,reject); //捕获的时候发生异常,执行reject
} catch (error) {
    reject(error)
}

可以在executor中抛出throw new Error('error')测试

链式回调的异常处理

then中无论是执行成功的回调还是失败回调,只要有返回结果,都会走到下一个then(根据不同返回结果进入下一个then的不同回调,规则我的另一偏文章juejin.cn/post/684490…

promise是通过then中返回新的promise来实现链式调用的,试想:一个新的promise是可以继续调用then方法的,补充then方法如下

Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this;
    let promise2; //then返回的新Promise
    if(self.status === 'fulfilled'){
        //onFilfilled会同步执行,并返回新的promise2
        promise2 = new Promise(function (resolve,reject) {
            onFulfilled(self.value);
        });
    }
    if(self.status === 'rejected'){
        promise2 = new Promise(function (resolve,reject) {
            onRejected(self.reason);
        });
    }

    if(self.status === 'pending'){
        promise2 = new Promise(function (resolve,reject) {
            self.onResolvedCallbacks.push( function(){
            //捕获异步回调时的异常,如果有进入promise2的失败态
                 try{
                    onFulfilled(self.value);
                }catch (e){
                    reject(e);
                }
            });
            self.onRejectedCallbacks.push( function(){
                 try{
                    onRejected(self.reason);
                }catch (e){
                    reject(e);
                }
            });
        });
    }
    return promise2;
}

上面代码第一个if语句,执行成功回调时,返回新的promise2,因为new promise时,executor会立即执行,所以onFulfilled(成功回调)方法会同步执行,并捕获其中的异常;第二个if语句失败回调同理。
第三个if语句,如果是异步回调,执行promise2的executor方法时,只是将这个成功的回调onFulfilled加入成功回调队列onResolvedCallbacks(失败同理),当成功回调真正执行时,如果发生异常,还需要捕获,并进入新promise2的失败态

链式回调的返回值处理

如果第一个promise返回一个普通值,会走到下一次then的成功的回调
如果第一个promise返回了一个promise,需要等待返回的promise执行后的结果,再传递给下一次then中。
所以我们用x接收第一个then的返回值 let x = onFulfilled(self.value);
x可能是普通值,也可能是promise,也可能是别人实现的promise, 这里实现一个resolvePromise方法统一处理返回值

then的代码更新如下 : 都用x接收回调函数的返回值,并调用resolvePromise来处理

Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this;
    let promise2;//then返回的新Promise
    if(self.status === 'fulfilled'){
        promise2 = new Promise(function (resolve,reject) {
               let x= onFulfilled(self.value);
               resolvePromise(promise2,x,resolve,reject);
        });
    }
    if(self.status === 'rejected'){
        promise2 = new Promise(function (resolve,reject) {
                let x= onRejected(self.reason);
                resolvePromise(promise2,x,resolve,reject);
           
        });
    }
    if(self.status === 'pending'){
        promise2 = new Promise(function (resolve,reject) {
            self.onResolvedCallbacks.push( function(){
                 try{
                    let x= onFulfilled(self.value);
                    resolvePromise(promise2,x,resolve,reject);
                }catch (e){
                    reject(e);
                }
            });
            self.onRejectedCallbacks.push( function(){
                 try{
                    let x= onRejected(self.reason);
                    resolvePromise(promise2,x,resolve,reject);
                }catch (e){
                    reject(e);
                }
            });
        });
    }
    return promise2;
}

resolvePromise的实现如下
参数:
p2 :第二次then的promise实例
x:第一次then的返回值
resolve/reject : p2的 resolve/reject

function resolvePromise(p2,x,resolve,reject){
   if(p2 === x){ //报一个类型错误
        return reject(new TypeError('循环引用了'));
   }
   //判断x是不是promise
   if(x!== null || (typeof x === 'object'||typeof x === 'function')){
        //x可能是promise   看对象中是否有then方法,有then就认为是promise
        //取then方法有可能异常,发生异常就进入p2的失败态
        try {
            let then = x.then;
            if(typeof then === 'function'){ 
                //认为x是一个promise,立刻执行该promise的then方法
                //如果x进入成功态,会触发成功回调
                then.call(x,function(y){ 
                    //y可能还是一个promise,继续解析,直到返回的是一个普通值
                    resolvePromise(p2,y,resolve,reject);
                    
                },function(err){ //如果x进入失败态,会触发p2的失败态
                    reject(err);
                });

            }else{ //如果then不是方法,直接认为返回一个对象,调用p2成功态
                resolve(x);
            }
        } catch (error) {
            reject(error);
        }
        
   }else{ //x是普通值,调用p2成功态
        resolve(x);
   }
};

多次调用resolve,reject 加入called标识

如果有人把代码写成

let p1= new Promise(function (resolve,reject) {
  resolve('success');
  reject('fail1');
});

promise的处理方式是,一旦进入成功态,就成功了,不会再调用reject,反之亦然
这里通过在resolvePromise方法中,加入called 标识,表示已经进入一个resolve或reject;若果called为true,直接返回,如果为false,置为true

function resolvePromise(p2, x, resolve, reject) {
    if (p2 === x) { //这里应该报一个类型错误,有问题
        return reject(new TypeError('循环引用了'))
    }
    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) {
                    if (called) return;
                    called = true;
                    resolvePromise(promise2, y, resolve, reject)
                }, function (err) { //失败
                    if (called) return;
                    called = true;
                    reject(err);
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) return;
            called = true;
            reject(e);
        }
    } else { // 说明是一个普通值1
        resolve(x); // 表示成功了
    }
}

Promise中值的穿透

我们可以在then中什么都不写

p1.then().then().then(function(data){
    console.log('data',data);
},function(err){
    console.log('err',err);
})

p1的执行结果依然会穿透到最后一个then的相应的回调
这需要在then方法中一开始就判断,是否有resolve/reject方法,如果没有,需要给默认的处理方法

onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : 
    function(value){ //默认的成功回调,返回一个值,就会进入下一个then的成功回调
      return value;
    };
onRejected = typeof onRejected === 'function' ? onRejected : 
    function(err){//默认的失败回调,抛出异常,就会进入下一个then的失败回调
        throw err;
    };

实现catch

捕获错误的方法,catch就相当于一个then调用错误方法

Promise.prototype.catch = function (callback) {
    return this.then(null,callback);
}

实现Promise.all

all接收一个成员为promise实例的数组,依次执行,并按顺序返回执行结果
当所有promise都执行成功,就进入成功态,有一个执行失败了,就进入失败态

 //promises是一个promise的数组
Promise.all = function (promises) {
    return new Promise(function (resolve, reject) {
        let arr = []; //arr是最终返回值的结果
        let count = 0; // 表示成功了多少次
        function processData(index, y) {
            arr[index] = y;
            if (++count === promises.length) {
                resolve(arr);
            }
        }
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(function (y) {
                processData(i, y)
            }, reject) //有一个失败,就调用失败回调
        }
    })
};

实现Promise.race

参数同all,只要有一个promise成功了,就算成功。如果有一个失败了,就失败了,其他promise继续执行

Promise.race = function (promises) {
    return new Promise(function (resolve, reject) {
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(resolve,reject)
        }
    })
};

实现Promise.resolve/Promise.reject

Promise.resolve 可以理解为 生成一个成功的promise

Promise.resolve = function(value){
    return new Promise(function(resolve,reject){
        resolve(value);
    })
}

Promise.reject 即生成一个失败的promise

Promise.reject = function(reason){
    return new Promise(function(resolve,reject){
        reject(reason);
    })
}

Promises/A+规范明确要求回调需要通过异步方式执行,用以保证一致可靠的执行顺序

这里用setTimeout模拟异步执行,将所有成功和失败的回调都用setTimeout包装,既然异步执行,还需捕获异常

最后Promise的实现如下(总)

function Promise(executor) { // executor是一个执行函数
    let self = this;
    self.status = 'pending';
    self.value = undefined; // 默认成功的值
    self.reason = undefined; // 默认失败的原因
    self.onResolvedCallbacks = []; // 存放then成功的回调
    self.onRejectedCallbacks = []; // 存放then失败的回调
    function resolve(value) { // 成功状态
        if (self.status === 'pending') {
            self.status = 'resolved';
            self.value = value;
            self.onResolvedCallbacks.forEach(function (fn) {
                fn();
            });
        }
    }
    function reject(reason) { // 失败状态
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
            self.onRejectedCallbacks.forEach(function (fn) {
                fn();
            })
        }
    }
    try {
        executor(resolve, reject)
    } catch (e) { // 捕获的时候发生异常,就直接失败了
        reject(e);
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    // 有可能这里返回的x是别人的promise
    // 尽可能允许其他乱写
    if (promise2 === x) { //这里应该报一个类型错误,有问题
        return reject(new TypeError('循环引用了'))
    }
    // 看x是不是一个promise,promise应该是一个对象
    let called; // 表示是否调用过成功或者失败
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        // 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
        try { // {then:1}
            let then = x.then;
            if (typeof then === 'function') {
                // 成功
                then.call(x, function (y) {
                    if (called) return
                    called = true
                    // y可能还是一个promise,在去解析直到返回的是一个普通值
                    resolvePromise(promise2, y, resolve, reject)
                }, function (err) { //失败
                    if (called) return
                    called = true
                    reject(err);
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true;
            reject(e);
        }
    } else { // 说明是一个普通值1
        resolve(x); // 表示成功了
    }
}
Promise.prototype.then = function (onFulfilled, onRjected) {
    //成功和失败默认不穿给一个函数
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value;
    }
    onRjected = typeof onRjected === 'function' ? onRjected : function (err) {
        throw err;
    }
    let self = this;
    let promise2; //返回的promise
    if (self.status === 'resolved') {
        promise2 = new Promise(function (resolve, reject) {
            // 当成功或者失败执行时有异常那么返回的promise应该处于失败状态
            // x可能是一个promise 也有可能是一个普通的值
            setTimeout(function () {
                try {
                    let x = onFulfilled(self.value);
                    // x可能是别人promise,写一个方法统一处理
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            })
        })
    }
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
            setTimeout(function () {
                try {
                    let x = onRjected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            })

        })
    }
    // 当调用then时可能没成功 也没失败
    if (self.status === 'pending') {
        promise2 = new Promise(function (resolve, reject) {
            // 此时没有resolve 也没有reject
            self.onResolvedCallbacks.push(function () {
                setTimeout(function () {
                    try {
                        let x = onFulfilled(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                })
            });
            self.onRejectedCallbacks.push(function () {
                setTimeout(function () {
                    try {
                        let x = onRjected(self.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                })
            });
        })
    }
    return promise2;
}
// 捕获错误的方法
Promise.prototype.catch = function (callback) {
    return this.then(null, callback)
}
// 解析全部方法
Promise.all = function (promises) {
    //promises是一个promise的数组
    return new Promise(function (resolve, reject) {
        let arr = []; //arr是最终返回值的结果
        let i = 0; // 表示成功了多少次
        function processData(index, y) {
            arr[index] = y;
            if (++i === promises.length) {
                resolve(arr);
            }
        }
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(function (y) {
                processData(i, y)
            }, reject)
        }
    })
};
// 只要有一个promise成功了 就算成功。如果第一个失败了就失败了
Promise.race = function (promises) {
    return new Promise(function (resolve, reject) {
        for (var i = 0; i < promises.length; i++) {
            promises[i].then(resolve,reject)
        }
    })
}
// 生成一个成功的promise
Promise.resolve = function(value){
    return new Promise(function(resolve,reject){
        resolve(value);
    })
}
// 生成一个失败的promise
Promise.reject = function(reason){
    return new Promise(function(resolve,reject){
        reject(reason);
    })
}
Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise(function (resolve, reject) {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd
}

module.exports = Promise;