Promise的實現及解析

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

promise接收一個executor函數,該函數在構造promise實例時就執行,因此在Promise構造方法中執行executor,executor須要接收resolve做爲執行成功時的回調函數,接收reject做爲執行失敗時的回調函數,因此定義了resolve和reject方法
bash

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;
複製代碼

測試用例2post

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中的成功回調都調用一遍,就是這段代碼ui

self.onResolvedCallbacks.forEach(function(fn){
    fn();
})
複製代碼

executor異常處理

當執行異步操做時有可能發生異常,須要try/catch捕獲到異常,並使promise進入rejected狀態this

try {
    executor(resolve,reject); //捕獲的時候發生異常,執行reject
} catch (error) {
    reject(error)
}
複製代碼

能夠在executor中拋出throw new Error('error')測試
url

鏈式回調的異常處理

then中不管是執行成功的回調仍是失敗回調,只要有返回結果,都會走到下一個then(根據不一樣返回結果進入下一個then的不一樣回調,規則個人另外一偏文章juejin.im/post/5aae65…

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;
複製代碼
相關文章
相關標籤/搜索