Promise核心實現

核心

構造函數核心

  • 維護狀態變量,只能由pending變爲resolve或者reject
  • 維護一個存儲結果的變量
  • 維護一個回調數組,執行到then,如果我們傳入的立即執行函數沒有立即執行resolve或者reject,所以promise的狀態還是pending,這時要把then裏面的回調函數保存起來。待到resolve或者reject執行後則執行回調數組裏存到方法。若傳入的立即執行函數直接執行了resolve或者reject此時就不用把回調保存起來,直接執行onResolved或onRejected方法。注意是異步執行。而且是做爲微任務的。(下面我們用setTimeout模擬)

then核心

  • 執行時,若當前狀態爲pending,則把回調保存到回調數組,若爲resolve或者reject則直接異步執行(微任務)。如果回調函數返回的不是promise,return的promise的狀態是resolved,value就是返回的值。即resolve(result)或者reject(result)
  • 每次then都返回一個新的Promise。
  • promise會發生值傳透,當then中傳入的不算函數,則這個then返回的promise的data,將會保存上一個的promise.data。這就是發生值穿透的原因。而且每一個無效的then所返回的promise的狀態都爲resolved。

核心實現

將Promise向外暴露

(function (window) {
    /*
    Promise構造函數
    executor:執行器函數
     */
    function Promise(executor) {

    }

    // 向外暴露Promise
    window.Promise = Promise
})()

構造函數實現

  function MyPromise(exector) {
    var self = this;
    self.status = 'pending'; // 給promise對象指定status屬性,初始值爲pending
    self.data = undefined; // 給promise對象指定一個存儲結果的data
    self.callbacks = []; // 每個元素的結構 {onResolved(){},onRejected(){}}
    function resolve(value) {
      if(self.status !== 'pending') {
        return
      }
      self.status = 'resolved'; // 將狀態更改爲resolved
      self.data = value; // 保存value的值
      // 異步調用resolve纔會在這裏執行
      // 如果有待執行的callback函數,立即異步執行回調函數onResolved();
      if(self.callbacks.length > 0) {
        self.callbacks.forEach(callbacksObj => {
          callbacksObj.onResolved(value);
        });
      }
    }

    function reject() {
      // 如果當前狀態不是pending,則不執行
      if(self.status !== 'pending'){
          return
      }
      // 將狀態改爲rejected
      self.status = 'rejected';
      // 保存value的值
      self.data = value;
      // 如果有待執行的callback函數,立即異步執行回調函數onResolved
      if (self.callbacks.length>0){
        self.callbacks.forEach(callbackObj=>{
            callbackObj.onRejected(value)
        })
      }
    }
    // 立即同步執行exector
    // 注意⚠️:當在執行executor的時候,如果執行異常的話,這個promise的狀態會直接執行reject方法
    try{
      // 立即同步執行executor
      executor(resolve,reject);
    } catch (e) {
      // 如果執行器拋出異常,promise對象變爲rejected狀態
      reject(e);
    }
  }

then實現

 MyPromise.prototype.then = function(onResolved, onReject) {
    var self = this;
    // 處理值穿透
    onResolved = typeof onResolved === 'function'? onResolved: value => value
    onRejected = typeof onRejected === 'function'? onRejected: reason => {throw reason}
    
    return new myPromise((resolve,reject) => {
      if(self.status === 'pending') {
        // promise當前狀態還是pending狀態,將回調函數保存起來
        self.callbacks.push({
          onResolved() {
            handle(onResolved)
          },
          onRejected() {
            handle(onRejected)
          }
        })
      } else if (self.status === 'resolved') {
        setTimeout(()=>{
          handle(onResolved)
        })
      } else { // 當status === 'rejected'
        setTimeout(()=>{
          handle(onRejected)
        })
      }
      // 處理函數
      function handle(callback) {
        try {
          const result = callback(self.data)
          if (result instanceof MyPromise){
            // 如果回調函數返回的是promise,return的promise的結果就是這個promise的結果
            result.then(
              value => {resolve(value)},
              reason => {reject(reason)}
            )
          } else {
            // 如果回調函數返回的不是promise,return的promise的狀態是resolved,value就是返回的值。
            resolve(result)
          }
        } catch (e) {
          //  如果執行onResolved的時候拋出錯誤,則返回的promise的狀態爲rejected
          reject(e)
        }
      }
    });
  }

其他方法實現

catch

借用then方法

MyPromise.prototype.catch = function(onRejected){
    return this.then(undefined,onRejected)
}

finally

借用then方法

  MyPromise.prototype.finally = (onFinally) => {
    return this.then((res)=>{
      MyPromise.resolve(onFinally()).then(()=> res)
    },(reson)=>{
      MyPromise.resolve(onFinally()).then(()=> reson)
    })
  }

resolve

Promise參數可以爲如下三種

  • 不是promise
  • 成功狀態的promise
  • 失敗狀態的promise
MyPromise.resolve = function(value){
  return new MyPromise((resolve,reject)=>{
      if (value instanceof Promise){
          // 如果value 是promise
          value.then(
              value => {resolve(value)},
              reason => {reject(reason)}
          )
      } else{
          // 如果value不是promise
          resolve(value)
      }
  }
}

reject

MyPromise.reject = function(reason) {
    return new MyPromise((resolve,reject)=>{
        reject(reason)
    })
}

all

  • 入參一般是個由Promise實例組成的數組,但是也可以不是數組,但必須具有 Iterator 接口,且返回的每個成員都是 Promise 實例。若參數如果不是 Promise 實例,就會先調用Promise.resolve()方法,將參數轉爲 Promise 實例,再進一步處理。
  • 返回值是個promise,因爲可以使用.then
  • 如果全部成功,狀態變爲resolved, 並且返回值組成一個數組傳給回調
  • 但凡有一個失敗,狀態變爲rejected, 並將error返回給回調
MyPromise.all = (promisesArr) => {
    // 返回Promise
    return new MyPromise((resolve, reject) => {
        let dataArr = new Array(promisesArr.length);
        let count = 0;
        for (let i = 0; i < promisesArr.length; i++) {
            // 在 .then 中收集數據,並添加 .catch,在某一個 Promise 遇到錯誤隨時 reject。
            // 這樣,在最外面調用 Promise.all().catch() 時也可以 catch 錯誤信息
          // 判斷當前這個元素是否爲Promise對象,不是則轉爲Promise對象
            let currentPromise = (promisesArr[i] instanceof Promise) ? promisesArr[i] : Promise.resolve(promisesArr[i]);
            currentPromise.then(res => { 
            dataArr[index] = data;
              count++;
              // 如果數據收集完了,就把收集的數據 resolve 出去
              if (count === promisesArr.length) resolve(dataArr);
        }).catch(err => { 
            //如果某一個失敗,promise.all()立即執行reject回調。
              //但剩餘的promise依舊繼續執行,只不過對promise.all的結果不會產生影響了
            reject(err) 
        });
        }
    })

注意⚠️:dataArr添加時用下標而不用數組時爲了防止順序錯亂

race

返回一個promise對象,狀態由第一個完成的promise決定

MyPromise.race = function(promisesArr){
    return new Promise((resolve,reject)=>{
        // 遍歷promises,獲取每個promise的結果
        for (let i = 0; i < promisesArr.length; i++) {
            // 判斷當前這個元素是否爲Promise對象,不是則轉爲Promise對象
            let currentPromise = (promisesArr[i] instanceof Promise) ? promisesArr[i] : Promise.resolve(promisesArr[i]);
            currentPromise.then(
                value => {
                    // 只要有一個成功,返回的promise的狀態九尾resolved
                    resolve(value)
                },
                reason => { //只要有一個失敗,return的promise狀態就爲reject
                    reject(reason)
                }
            )
        })
    })
}

allSettled

Promise.allSettled() 方法返回一個在所有給定的 promise 已被決議或被拒絕後決議的 promise,並帶有一個對象數組,每個對象表示對應的promise 結果。

Promise.newAllSettled = function (promisesArr) {
  return new Promise((resolve, reject) => {
    let results = [];
    let count = 0;
    let promisesArrLength = promisesArr.length;
    // 運行所有的 Promise
    for (let i = 0; i < promisesArr.length; i++) {
      // 判斷當前這個元素是否爲Promise對象,不是則轉爲Promise對象
      let currentPromise = (promisesArr[i] instanceof Promise) ? promisesArr[i] : Promise.resolve(promisesArr[i]);
      currentPromise.then(res => {
        // 當有 Promise 被 resolve 之後,記錄 resolve 值和狀態,已決 Promise 計數加一
        results.push({value: res, status: 'fulfilled'});
        count++;
        // 全部 Promise 已決,resolve
        if (count === promisesArrLength) {
          resolve(results);
        }
      }).catch(err => {
        // 當有 Promise 被 reject 後,記錄 reject 值和狀態,並且已決的 Promise 計數加一
        results.push({value: err, status: 'rejected'});
        count++;
        if (count === promisesArrLength) {
          resolve(results);
        }
      });
    }
  })
};

參考

https://juejin.im/post/6844904088963022856

https://juejin.im/post/6850037281206566919

https://blog.csdn.net/MichelleZhai/article/details/104475521

https://zhuanlan.zhihu.com/p/60287801

https://zhuanlan.zhihu.com/p/41502945

https://zhuanlan.zhihu.com/p/61681036

https://juejin.im/post/6856213486633304078

https://zhuanlan.zhihu.com/p/107712626

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章