Promise.then是如何實現鏈式調用的

前言

咱們都知道,then 方法返回一個新的 promise 實例,這是實現鏈式調用的根本。javascript

爲了在 promise 狀態發生變化時(resolve / reject 被調用時)再執行 then 裏的函數,咱們使用一個 callbacks 數組先把傳給then的函數暫存起來,等狀態改變時再調用。java

那麼,怎麼保證後一個 then 裏的方法在前一個 then(多是異步)結束以後再執行呢?數組

好比下面示例中,最後一個 then 應該在 2 秒後再打印,值爲 2promise

var p1 = new MyPromise((resolve, reject) => {
      console.log('hhh')
      setTimeout(() => {
        resolve(1)
      }, 1000);
    }).then(res => {
      return new MyPromise((resolve, reject) => {
        setTimeout(() => {
          resolve(res+1)
        }, 1000);
      })
    }).then(res => {
      console.log(res);
      return res;
    })
複製代碼

實現鏈式調用的核心

將傳給 then 的函數和新 promiseresolve 一塊兒 push 到前一個 promisecallbacks 數組中,達到承前啓後的效果markdown

  • 承前:當前一個 promise 完成後,調用其 resolve 變動狀態,在這個 resolve 裏會依次調用 callbacks 裏的回調,這樣就執行了 then 裏的方法了
  • 啓後:上一步中,當 then 裏的方法執行完成後,返回一個結果,若是這個結果是個簡單的值,就直接調用新 promiseresolve,讓其狀態變動,這又會依次調用新 promisecallbacks 數組裏的方法,循環往復。。若是返回的結果是個 promise,則須要等它完成以後再觸發新 promiseresolve,因此能夠在其結果的 then 裏調用新 promiseresolve

then的代碼實現:

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(err){
          reject(err)
        }
      }
      // 封裝前一個promise失敗時執行的函數
      let rejected = () => {
        try{
          const result = onReject(self.reason);
          return result instanceof MyPromise? result.then(resolve, reject) : reject(result);
        }catch(err){
          reject(err)
        }
      }

      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 數組中提早註冊的回調

那麼,什麼狀況下一個 callbaks 數組中會有多個回調呢?異步

好比這種:
p1 完成時,註冊在 p1callbacks 中的函數依次執行
如下代碼 1 秒後打印出 1, 2 秒後打印出 2函數

var p1 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve(1)
    }, 1000);
  })
var p2 =p1.then(res => {  //p1完成時的回調1
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve(res+1)
    }, 1000);
  })
}).then(res => {
  console.log(res); // 2
  return res+1;
})

p1.then(res => {  // p1完成時的回調2
    console.log(res);  // 1
})
複製代碼

可是,若是這麼寫:ui

var p1 = new MyPromise((resolve, reject) => {
      console.log('hhh')
      setTimeout(() => {
        resolve(1)
      }, 1000);
    }).then(res => {
      return new MyPromise((resolve, reject) => {
        setTimeout(() => {
          resolve(res+1)
        }, 1000);
      })
    }).then(res => {
      console.log(res); // 2
      return res+1;
    })

p1.then(res => {
    console.log(res);  // 3
})
複製代碼

最後一個 p1.then 是在最後纔打印出來的,由於 p1 的返回值是第三個 promise,最後一個 then 裏的函數也是註冊進了第三個 promisecallbacks 中。this

完整的 Promise 實現

// 定義三種狀態
 const PENDING = 'PENDING';
 const FULFILLED = 'FULFILLED';
 const REJECT = 'REJECT';

 class MyPromise {
   constructor(fn){
     // 初始化狀態
     this.status = PENDING;
     // 將成功、失敗的結果放在this上,便於then、catch訪問
     this.value = null;
     this.reason = null;

     // 成功態、失敗態回調函數隊列,同步調用then時將對應態的函數註冊進去, 在狀態變動的時候調用
     this.onFulfilledCallbacks = [];
     this.onRejectedCallbacks = [];

     const resolve = (value) => {
       if(this.status === PENDING){
         this.status = FULFILLED;
         this.value = value;
         // 成功態回調函數依次執行
         this.onFulfilledCallbacks.forEach(fn => fn(this.value))
       }
     }
     const reject = (reason) => {
      if(this.status === PENDING){
        this.status = REJECT;
        this.reason = reason;
        // 失敗態回調函數依次執行
        this.onRejectedCallbacks.forEach(fn => fn(this.reason))
      }
     }
     // 生成實例後當即調用fn
     // 把內部的resolve和reject傳入fn,用戶可調用resolve和reject
     try{
      fn(resolve, reject); 
     }catch(err){
       // fn執行出錯,將錯誤內容用reject拋出去
       reject(err)
     }
     
   }
   then(onFulfilled, onReject){
    // 實現值穿透 當then中傳入的不是函數,則這個promise返回上一個promise的值
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onReject = typeof onReject === 'function' ? onReject : reason => { throw new Error(reason) }

    // 保存前一個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(err){
          reject(err)
        }
      }
      // 封裝前一個promise失敗時執行的函數
      let rejected = () => {
        try{
          const result = onReject(self.reason);
          return result instanceof MyPromise? result.then(resolve, reject) : reject(result);
        }catch(err){
          reject(err)
        }
      }

      switch(self.status){
        case PENDING: 
          self.onFulfilledCallbacks.push(fulfilled);
          self.onRejectedCallbacks.push(rejected);
          break;
        case FULFILLED:
          fulfilled();
          break;
        case REJECT:
          rejected();
          break;
      }
    })
   }

   // Promise.prototype.catch就是Promise.prototype.then(null, onRejected)的別名
   catch(onRejected){
     return this.then(null, onRejected);
   }

  static resolve(value){
    // 若是是promise實例,直接返回
    if(value instanceof MyPromise){
      return value;
    }else{
      // 若是不是promise實例,返回一個新的promise對象,狀態爲fulfilled
      return new MyPromise((resolve, reject) => resolve(value))
    }
  }
  static reject(reason){
    // Promise.reject方法的參數會原封不動地做爲reject的參數
      return new MyPromise((resolve, reject) => reject(reason))
  }

  /** * Promise.all() 接受一個數組,返回一個promise對象 * 全部的promise狀態變爲FULFILLED,返回的promise狀態才變爲FULFILLED。 * 一個promise狀態變爲REJECTED,返回的promise狀態就變爲REJECTED。 * 數組成員不必定都是promise,須要使用Promise.resolve()處理。 */
  static all(promiseArr){
    const len = promiseArr.length;
    const values = new Array(len);

    let count = 0; // 記錄已經成功的promise個數
    return new MyPromise((resolve, reject) => {
      for(let i=0; i<len; i++){
        // Promise.resolve()處理,確保每個都是promise實例
        MyPromise.resolve(promiseArr[i]).then(
          val => {
            values[i] = val;
            count++;
            if(count === len) resolve(values); // 若是所有執行完,改變promise的狀態爲FulFilled
          },
          err => {
            reject(err)
          }
        )
      }
    })
  }
  static race(promiseArr){
    return new MyPromise((resolve, reject) => {
      promiseArr.forEach(item => {
        MyPromise,resolve(item).then(
          val => resolve(val),
          err => reject(err)
        )
      })
    })
  }
 }
複製代碼
相關文章
相關標籤/搜索