實現一個promise

晴天~ ☀發表於2018-12-11
在大前端,越來越人來探究promise背後的真面目,今天我們就來實現一個符合promiseA+規範的promise。promise上的靜態方法有resolve()、reject()、all()、race(),原型上的方法有then()、catch()、finally()。

1、基礎框架

promiser的建構函式接收一個引數是executor函式,並且傳入兩個引數:resolve,reject。分別是非同步操作成功的回撥函式和非同步操作失敗後的回撥函式。

promise有三個狀態 等待(pending) 成功態 (fulfilled) 失敗態(rejected)

預設情況是 pending ->  fulfilled
          pending ->  rejected 
          fulfilled不能和rejected相互轉化複製程式碼

下面來看一下程式碼具體的實現:

function Promise(executor){  
    let self = this;  
    self.status = 'pending'  
    self.value = undefined;  
    self.reason = undefined;  
    self.onResolvedCallbacks = []; //非同步時,存放成功的回撥函式  
    self.onRejectedCallbacks = []; //非同步時,存放失敗的回撥函式  
    // 只有狀態是pending 參能進行狀態的轉化  
    function resolve(value) {    
        if(self.status === 'pending'){      
            self.value = value;      
            self.status = 'fulfilled';      
            self.onResolvedCallbacks.forEach(function (fn) {        
                fn(); //成功時呼叫迴圈呼叫成功的函式執行      
            });   
         } 
    }  
    function reject(reason) {    
        if(self.status === 'pending'){      
            self.reason = reason;      
            self.status = 'rejected';      
            self.onRejectedCallbacks.forEach(function (fn) {        
                fn();//失敗時呼叫迴圈呼叫失敗的函式執行      
            })    
        }  
    }  
    try{    
        executor(resolve, reject); // 如果執行這個executor執行時候丟擲異常 應該走下一個then的失敗  
    }catch(e){    
        reject(e);// 出錯了   
    }
}複製程式碼

2、then方法

每個promise例項上都有一個then方法,then方法接受兩個引數,onFulfilled和onRejected。判斷狀態成功的時候(fulfilled),呼叫onFulfilled函式,把成功的值傳回。判斷狀態失敗的時候(rejected),呼叫onRejected失敗函式,把失敗的值傳回。

如果狀態是pending,代表是非同步執行,把onFulfilled和onRejected函式存起來,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);    
        })  
    }}
複製程式碼

3、then方法鏈式呼叫

  • promise每次呼叫then後,返回一個新的promise(then方法成功後 返回的是一個新的promise,這個返回的promise會被執行,如果返回的promise是成功的,會把這個結果傳遞到外層的下一個then中)
  • 如果返回的是promise 用promise的成功或者失敗 執行下一個then
  • 如果返回的是一個普通值 會走外層下一個then的成功
  • 如果執行的時候 丟擲異常就會走到下一個次then中的失敗
  • then中可以不傳遞引數,如果不傳遞 會透到下一個then中
Promise.prototype.then = function (onFulfilled, onRejected) {  
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled:function (data) {    return data  }  
    onRejected = typeof onRejected === 'function' ? onRejected:function (err) {    throw err;  }  
    let self = this;  
    let promise2; //promise2就是我們每次呼叫then後返回的新的promise
    promise2 = new Promise(function (resolve,reject) {    
        if (self.status === 'fulfilled') {      
            // 這個返回值是成功函式的執行結果      
            setTimeout(() => {        
                try{          
                    let x = onFulfilled(self.value);            
                    resolvePromise(promise2, x, resolve, reject);        
                }catch(e){          
                    reject(e);        
                }      
            }, 0);    
        }    
        if (self.status === 'rejected') {      
            setTimeout(() => {        
               try{          
                    let x = onRejected(self.reason);          
                    resolvePromise(promise2, x, resolve, reject);        
                    }catch(e){          
                    reject(e)        
                }      
            },0)    
        }    
        if (self.status === 'pending') {      
            // 預設當前 new Promise  executor中是有非同步的      
            self.onResolvedCallbacks.push(function () {        
                setTimeout(() => {          
                    try{            
                        let x = onFulfilled(self.value);            
                        resolvePromise(promise2, x, resolve, reject);          
                    }catch(e){            
                        reject(e)          
                    }        
                }, 0);      
             });    
            self.onRejectedCallbacks.push(function () {        
                setTimeout(() => {          
                    try{           
                        let x = onRejected(self.reason);            
                        resolvePromise(promise2, x, resolve, reject);          
                    }catch(e){
                        reject(e);          
                    }        
                }, 0);      
            })    
        }  
    });  
    return promise2;  
}複製程式碼

4、鏈式呼叫的核心方法

核心方法,處理成功或者失敗執行的返回值和promise2的關係

function resolvePromise(promise2,x,resolve,reject) {  
    // 這個處理函式 需要處理的邏輯韓式很複雜的  
    // 有可能這個x 是一個promise  但是這個promise並不是我自己的  
    if(promise2 === x){   
        return reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))  
    }  
    // 不單單需要考慮自己 還要考慮 有可能是別人的promise  let called; 
    // 文件要求 一旦成功了 不能呼叫失敗  
    if((x!=null&&typeof x=== 'object') || typeof x === 'function'){    
        // 這樣只能說 x 可能是一個promise    
        try{      
            // x = {then:function(){}}    
            let then = x.then; // 取then方法      
            if(typeof then === 'function'){        
                then.call(x,function (y) { 
                    // resolve(new Promise)          
                    if(!called){called = true;} else{ return;}          
                    resolvePromise(x,y,resolve,reject); //  遞迴檢查promise        
                },function (r) {          
                    if (!called) { called = true; } else { return; }          
                        reject(r);        
                });      
            }else{ // then方法不存在        
                resolve(x); // 普通值      
            }    
        }catch(e){ // 如果取then方法出錯了,就走失敗      
            if (!called) { called = true; } else { return; }      
            reject(e);    
        }  
    }else{    
        resolve(x);  
    }
}複製程式碼

5、catch方法的實現

catch方法其實就是then方法,直接呼叫then方法,成功傳null,失敗把引數傳進去就ok了。

Promise.prototype.catch = function (onRejected) {  
    return this.then(null, onRejected);
}複製程式碼

6、finally方法

無論成功還是失敗,都要執行回撥函式,並且把成功或者失敗的值向下傳遞。

Promise.prototype.finally = function (cb) {   
    return this.then(function (data) {    
        cb();    
        return data;  
    }, function (err) {
        cb();    
        throw err;  
    });
}複製程式碼

7、reject方法實現

Promise.reject = function (reason) {  
    return new Promise(function (resolve, reject) {    
        reject(reason);  
    })
}複製程式碼

8、resolve方法實現

Promise.resolve = function (value) {  
    return new Promise(function (resolve, reject) {    
        resolve(value);  
    })
}複製程式碼

9、all方法實現

Promise提供了一個 併發的方法 Promise.all 實現併發執行promise.all方法返回的結果是一個promise

Promise.all = function (promises) {  
    return new Promise(function (resolve, reject) {    
        let arr = [];    // 處理資料的方法    
        let i = 0;    
        function processData(index, data) {      
            arr[index] = data; //陣列的索引和長度的關係      
            if (++i === promises.length) { 
                // 當陣列的長度 和promise的個數相等時 說明所有的promise都執行完成了        
                resolve(arr);      
            }    
        }    
        for (let i = 0; i < promises.length; i++) {      
            let promise = promises[i];      
            if (typeof promise.then == 'function') {       
                promise.then(function (data) {          
                    processData(i, data); // 把索引和資料 對應起來 方便使用        
                }, reject)      
            } else {        
                processData(i, promise);      
            }    
        }  
    });
}複製程式碼

10、race方法實現

Promise.race傳一個陣列過去,只返回一個結果,哪個快返回哪個。

Promise.race = function (promises) {  
    return new Promise(function (resolve, reject) {    
        for (let i = 0; i < promises.length; i++) {      
            let promise = promises[i];      
            if (typeof promise.then == 'function') {        
                promise.then(resolve, reject)      
            } else {        
                resolve(promise);      
            }    
        }  
    })
}複製程式碼


相關文章