Promise原理解析

williamslau發表於2019-02-27

Promise是非同步程式設計的一種解決方案,比回撥函式和事件更合理更強大。

原生的promise用法如下:

let p=new Promise(function(resolve,reject){
    resolve('成功');
    //reject('失敗');
});
p.then(function(data){
    console.log(data);
},function(err){
    console.log(err);
});
複製程式碼

image

Promise例項中有一個函式,接受兩個引數,一個是成功的方法,一個是失敗的方法,然後有一個then方法,第一個是成功執行的函式,第二個是失敗執行的函式,並且會接收相應的引數。一旦執行成功,就不會再執行失敗,反之亦然。

function Promise(executor){  //executor是一個執行函式
    let self = this;
    self.status = 'pending';
    self.value = undefined; //預設成功的值
    self.reason = undefined; //預設失敗的值
    function resolve(value){ //成功狀態
        if(self.status === 'pending'){
            self.status = 'resolved';
            self.value = value;
        }
    }
    function reject(reason){  //失敗狀態
        if(self.status === 'pending'){
            self.status = 'rejected';
            self.reason = reason;
        }
    }
    try{
        executor(resolve,reject);
    }catch(e){      //處理異常狀態,傳給reject
        reject(e);
    }
};

Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this;
    if(self.status === 'resolved'){
        onFulfilled(self.value);
    }
    if(self.status === 'rejected'){
        onRejected(self.reason);
    }
};
module.exports = Promise;
複製程式碼

首先先定義一個Promise函式,並且接受一個executor執行函式,將resolve和reject傳參傳進去,定義私有屬性status,value,reason。                     Promise有三個狀態,status用來記錄他們:                     初始狀態為pending                     成功狀態為resolved                     失敗狀態為rejected

然後使用prototype掛載一個then方法,                     方法中要傳入一個成功的回掉和一個失敗的回掉,                     如果成功就調取成功的回掉,                     失敗就調取失敗的回掉

最後將函式匯出,就實現了一個最基礎功能的Promise。

但是這個Promise還是一個同步的方法,如果想在程式碼中使用非同步,比如:

let p=new Promise(function(resolve,reject){
    setTimeout(function(){
        resolve('成功');
    },1000);
    //throw new Error('錯誤');
});
複製程式碼

我們就需要將他變為非同步的:

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 = '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
        reject(e);
    }
}

Promise.prototype.then=function(onFulfilled,onRejected){
    let self = this;
    if(self.status === 'resolved'){
        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;
複製程式碼

如果他是非同步的,我們就不能在then方法中用status的成功失敗狀態來判斷他走的那,因為then方法執行後有可能params中還沒執行resolve或者reject,那麼我們就在Promise例項上再新增onResolvedCallbacks和onRejectedCallbacks兩個陣列來存放他的回掉,如果執行的是成功,失敗就會存預設的undefined。在呼叫時用forEach迴圈陣列依次知性方法。 原生的Promise還可以then多次:

function Promise(executor){
    let self = this;
    self.status = 'pending';
    self.value = undefined;
    self.reason = undefined;
    self.onResolvedCallbacks= [];
    self.onRejectedCallbacks= [];
    function resolve(value){
        self.status = 'resolved';
        self.value = value;
        self.onResolvedCallbacks.forEach(function(fn){
            fn();
        });
    }
    function reject(reason){
        self.status = 'rejected';
        self.reason = reason;
        self.onRejectedCallbacks.forEach(function(fn){
            fn();
        });
    }

    try{
        executor(resolve,reject);
    }catch(e){
        reject(e);
    }
};

Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this;
    let promise2;           //實現鏈式操作
    if(self.status === 'resolved'){
        promise2 = new Promise(function(resolve, reject){
            try{
                let x = onFulfilled(self.value);
                resolve(x);
            }catch(e){
                reject(e);
            }
        });
    }
    if(self.status === 'rejected'){
        promise2 = new Promise(function(resolve, reject){
            try{
                let x = onRejected(self.reason);
                reject(x);
            }catch(e){
                reject(e);
            }
        });
    }
    if(self.status === 'pending'){
        promise2 = new Promise(function(resolve, reject){
            self.onResolvedCallbacks.push(function(){
                try{
                    let x = onFulfilled(self.value);
                    resolve(x);
                }catch(e){
                    reject(e);
                }
            });
            self.onRejectedCallbacks.push(function(){
                try{
                    let x = onRejected(self.reason);
                    reject(x);
                }catch(e){
                    reject(e);
                }
            });
        });
    }
    return promise2;
}
module.exports = Promise;
複製程式碼

當然他then的鏈式呼叫不會有then屬性,所以我們可以判斷Promise每次then都會new一個新的Promise我們用Promise2來表示,then的時候new一個新的Promise並且return給下一個then。 then中無論是成功的回掉還是失敗的毀掉,只要返回了結果,就會走下一個then中的成功,發生錯誤才會走下一個then中的失敗,then中可以return普通值,也可return一個新的Promise,還有可能return 一個{then:xxx},當然更有promise.then().then.then(function(){});這種奇葩的用法

function Promise(executor){
    let self = this;
    self.status = 'pending';
    self.value = undefined;
    self.reason = undefined;
    self.onResolvedCallbacks= [];
    self.onRejectedCallbacks= [];
    function resolve(value){
        self.status = 'resolved';
        self.value = value;
        self.onResolvedCallbacks.forEach(function(fn){
            fn();
        });
    }
    function reject(reason){
        self.status = 'rejected';
        self.reason = reason;
        self.onRejectedCallbacks.forEach(function(fn){
            fn();
        });
    }
    try{
        executor(resolve,reject);
    }catch(e){
        reject(e);
    }
};

function resolvePromise(p2,x,resolve,reject){
    //1.處理亂寫
    //2.判斷返回的是不是自己
    if(p2 === x){
        reject(new typeError('迴圈引用'));
    }
    //判斷x是不是params(判斷x是不是object)
    let called; //表示是否呼叫過成功或者失敗
    if(x !== null || typeof x === 'object' || typeof x === 'function'){
        //判斷promise只要判斷物件中是否有then方法
        try{
            let then = x.then;
            if(typeof then === 'function'){ //then返回的可能是{then:xxx},判斷then是不是一個函式
                then.call(x,function(y){ //成功了以後可能會執行resolve(new Promise())用遞迴來解決
                    if(called) return;
                    called = true;
                    resolvePromise(p2,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{  //esle普通值
        resolve(x);
    }
}

Promise.prototype.then = function(onFulfilled,onRejected){  //判斷onFulfilled是不是一個函式,不是給他個函式
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value){
        return value;
    }
    onRejected = typeof onRejected === 'function' ? onRejected : function(err){
        throw err;
    }
    let self = this;
    let promise2;           //實現鏈式操作
    if(self.status === 'resolved'){
        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(){
                let x = onFulfilled(self.value);
                resolvePromise(promise2,x,resolve,reject);
            });
            self.onRejectedCallbacks.push(function(){
                let x = onRejected(self.reason);
                resolvePromise(promise2,x,resolve,reject);
            });
        });
    }
    return promise2;
}
module.exports = Promise;
複製程式碼

我們就定義一個resolvePromise來處理then中的返回結果,如果返回的是個錯誤資訊,就用try{}catch(){}讓他走reject 最後的最後,Promise中可以非同步執行程式碼,then方法中應該也可以實現非同步,很簡單,只要在相應的位置加上setTimeout就ok了,記得不要忘了加上try{}catch(){}來過濾錯誤資訊並且傳到reject中

function Promise(executor){
    let self = this;
    self.status = 'pending';
    self.value = undefined;
    self.reason = undefined;
    self.onResolvedCallbacks= [];
    self.onRejectedCallbacks= [];
    function resolve(value){
        self.status = 'resolved';
        self.value = value;
        self.onResolvedCallbacks.forEach(function(fn){
            fn();
        });
    }
    function reject(reason){
        self.status = 'rejected';
        self.reason = reason;
        self.onRejectedCallbacks.forEach(function(fn){
            fn();
        });
    }
    try{
        executor(resolve,reject);
    }catch(e){
        reject(e);
    }
};

function resolvePromise(p2,x,resolve,reject){
    //1.處理亂寫
    //2.判斷返回的是不是自己
    if(p2 === x){
        reject(new typeError('迴圈引用'));
    }
    //判斷x是不是params(判斷x是不是object)
    let called; //表示是否呼叫過成功或者失敗
    if(x !== null || typeof x === 'object' || typeof x === 'function'){
        //判斷promise只要判斷物件中是否有then方法
        try{
            let then = x.then;
            if(typeof then === 'function'){ //then返回的可能是{then:xxx},判斷then是不是一個函式
                then.call(x,function(y){ //成功了以後可能會執行resolve(new Promise())用遞迴來解決
                    if(called) return;
                    called = true;
                    resolvePromise(p2,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{  //esle普通值
        resolve(x);
    }
}

Promise.prototype.then = function(onFulfilled,onRejected){  //判斷onFulfilled是不是一個函式,不是給他個函式
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value){
        return value;
    }
    onRejected = typeof onRejected === 'function' ? onRejected : function(err){
        throw err;
    }
    let self = this;
    let promise2;           //實現鏈式操作
    if(self.status === 'resolved'){
        promise2 = new Promise(function(resolve, reject){
            setTimeout(function(){
                try{
                    let x = onFulfilled(self.value);
                    resolvePromise(promise2,x,resolve,reject);
                }catch(e){
                    reject(e);
                }
            })
        });
    }
    if(self.status === 'rejected'){
        promise2 = new Promise(function(resolve, reject){
            setTimeout(function(){
                try{
                    let x = onRejected(self.reason);
                    resolvePromise(promise2,x,resolve,reject);
                }catch(e){
                    reject(e);
                }
            })
        });
    }
    if(self.status === 'pending'){
        promise2 = new Promise(function(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 = onRejected(self.reason);
                        resolvePromise(promise2,x,resolve,reject);
                    }catch(e){
                        reject(e);
                    }
                })
            });
        });
    }
    return promise2;
}
module.exports = Promise;
複製程式碼

相關文章