JavaScript手寫Promise、Promise.then()、Promise.all()、Promise.race()

我始終認為要想真正的理解一個API也好,內置對象也罷,最好的方式就是手寫一遍。

手寫Promise

    const PENDING = "pending";    const RESOLVED = "resolved";    const REJECTED = "rejected";    function MyPromise() {      //保存初始化狀態      let selt = this;      //初始化狀態      this.state = PENDING;      //用於保存resolve或者rejected傳入的值      this.value = null;      //用於保存resolve的回調函數      this.resolvedCallbacks = [];      //用於保存reject的回調函數      this.rejectedCallbacks = [];      //狀態轉變為resolved的方法      function resolve(value) {        //判斷傳入的元素是否為Promise值,如果是,則狀態改變必須等待前一個狀態改變後再進行改變        if (value instanceof MyPromise) {          return value.then(resolve, reject);        }        //保證代碼的執行順序為本輪事件循環的末尾        setTimeout(() => {          //隻有狀態為pending時才能轉變          if (self.state === PENDING) {            //修改狀態            self.state = RESOLVED;            //設置傳入的值            self.value = value;            //執行回調函數            self.resolvedCallbacks.forEach((callback) => {              callback(value);            });          }        }, 0);      }      //狀態轉變為rejected方法      function reject(value) {        //保證代碼的執行順序為本輪事件循環的末尾        setTimeout(() => {          //隻有狀態為pending時才能轉變          if (self.state === PENDING) {            //修改狀態            self.state = REJECTED;            //設置傳入的值            self.value = value;            //執行回調函數            self.rejectedCallbacks.forEach((callback) => {              callback(value);            });          }        }, 0);      }      //將兩個方法傳入函數執行      try {        fn(resolve, reject);      } catch (e) {        //遇到錯誤時,捕獲錯誤,執行reject函數        reject(e);      }      MyPromise.prototype.then = function (onResolved, onRejected) {        //首先判斷兩個參數是否為函數類型,因為這兩個參數是可選參數        onResolved =          typeof onResolved === "function"            ? onResolved            : function (value) {                return value;              };        onRejected =          typeof onRejected === "function"            ? onRejected            : function (error) {                throw error;              };        //如果是等待狀態,則將函數加入對應列表中        if (this.state === PENDING) {          this.resolvedCallbacks.push(onResolved);          this.rejectedCallbacks.push(onRejected);        }        //如果狀態已經凝固,則直接執行對應狀態的函數        if (this.state === RESOLVED) {          onResolved(this.value);        }        if (this.state === REJECTED) {          onRejected(this.value);        }      };    }

手寫Promise.then

then方法返回一個新的Promise實例,為瞭在Promise狀態發生變化時再執行then裡的函數,我們使用一個callbacks數組先把傳給then的函數暫存起來,等狀態改變時再調用

那麼,怎麼保證後一個then裡的方法在前一個then(可能是異步)結束之後再執行呢?

我們可以將傳給then函數和新Promiseresolve一起push到前一個Promisecallbacks數組中,達到承前啟後的效果:

  • 承前:當前一個Promise完成後,調用其resolve變更狀態,在這個resolve裡會依次調用callbacks裡的回調,這樣就執行瞭then裡的方法
  • 啟後:上一步中,當then裡的方法執行完成後,返回一個結果,如果這個結果是個簡單的值,就直接調用新Promiseresolve,讓其狀態變更,這又會依次調用新Promisecallbacks數組裡的方法,循環往復。如果返回的結果是個Promise,則需要等它完成之後再出發新Promiseresolve,所以可在其結果的then裡調用新Promiseresolve
 then(onFulfilled, onReject) {      //保存前一個Promise的this      const self = this;      return new MyPromise((resolve, reject) => {        //封裝前一個Promise成功時執行的函數        let fulfilled = () => {          try {            const result = onFulfilled(self.value); //承前            return result instanceof MyPromise              ? result.then(resolve, reject)              : resolve(result); //啟後          } catch (e) {            reject(e);          }        };        //封裝前一個Promise失敗時執行的函數        let rejected = () => {          try {            const result = onRejected(self.reason);            return result instanceof MyPromise              ? result.then(resolve, reject)              : reject(result);          } catch (e) {            reject(e);          }        };        switch (self.status) {          case PENDING:            self.onFulfilledCallbacks.push(fulfilled);            self.onRejectedCallbacks.push(rejected);            break;          case FULFILLED:            fulfilled();            break;          case REJECT:            rejected();            break;        }      });    };

註意:

  • 連續多個then裡的回調方法是同步註冊的,但註冊到瞭不同的callbacks數組中,因為每次then都返回新的Promise實例
  • 註冊完成後開始執行構造函數中的異步事件,異步完成之後依次調用callbacks數組中提前註冊的回調

手寫Promise.all

  • 接收一個Promise實例的數組或具有Iterator接口的對象作為參數
  • 這個方法返回一個新的Promise對象
  • 遍歷傳入的參數,用Promise.resolve()將參數“包一層”,使其變成一個Promise對象
  • 參數所有回調成功才是成功,返回值數組與參數順序一致
  • 參數數組其中一個失敗,則觸發失敗狀態,第一個觸發失敗狀態的Promise錯誤信息作為Promise.all的錯誤信息
    function promiseAll(promises){     return new Promise(function(resolve, reject){       if(!Array.isArray(promises)){         throw new TypeError(`argument must be a array`)       }       let resolvedCounter =0;//已resolve的promise數       let promiseNum=promises.length;       let resolvedResult =[];//暫存resolve結果的數組       for(let i=0;i<promiseNum;i++){         Promise.resolve(promises[i]).then(value=>{           resolvedCounter++;           resolvedResult[i]=value;           if(resolvedCounter==promiseNum){             return resolve(resolvedResult)           }         },error=>{           return reject(error)         })       }     })   }//test   let p1=new Promise(function(resolve, reject){       setTimeout(function(){         resolve(1)       },1000)   });   let p2=new Promise(function(resolve, reject){       setTimeout(function(){         resolve(2)       },2000)   });   let p3=new Promise(function(resolve, reject){       setTimeout(function(){         resolve(3)       },3000)   });   promiseAll([p3,p1,p2]).then(res=>{     console.log(res);//[3,1,2]   })

手寫Promise.race

該方法的參數是Promise實例數組,然後其then註冊的回調方法是數組中的某一個Promise的狀態變為fufilled的時候執行。因為Promise的狀態隻能改變一次,那麼我們隻需要把Promise.race中產生的Promise對象的resolve,註入到數組中的每一個Promise實例中的回調函數即可。

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

手寫Promise.finally

finally (onFinally) {    return new Promise((resolve, reject) => {      this.then((result) => {        onFinally();        resolve(result);      }, (reason) => {        onFinally();        reject(reason);      });    });  }
本文來自網絡,不代表程式碼花園立場,如有侵權,請聯系管理員。https://www.codegarden.cn/article/25484/
返回顶部