Promise源碼學習(2)

Promise源碼學習(2)

本篇爲上一篇源碼學習(1)的補充,主要是來介紹Promise.all()和Promise.race()方法。
閒話少敘,進入正題git

Promise.race()

首先來簡單介紹一下功能吧,詳細好比可見阮一峯老師的ES6書籍。
Promise.race方法是將多個 Promise 實例,包裝成一個新的 Promise 實例。es6

const p = Promise.race([p1, p2, p3]);

上面代碼中,只要p一、p二、p3之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調函數。
接下來貼源代碼github

export default function race (entries) {
  /*jshint validthis:true */
  let Constructor = this;

  if (!isArray(entries)) {
    return new Constructor((_resolve, reject) => reject(new TypeError('You must pass an array to race.')));
  } else {
    return new Constructor((resolve, reject) => {//new 執行一次
      let length = entries.length;
      for (let i = 0; i < length; i++) {//執行每個傳入的entry 但只有最快的一個能resolve或reject改變返回的promise的狀態
        Constructor.resolve(entries[i]).then(resolve, reject);
      }
    });
  }
}
//isArray定義:
if (Array.isArray) {
  _isArray = Array.isArray;
} else {
  _isArray = x => Object.prototype.toString.call(x) === '[object Array]';
}

若是傳入的參數不是數據直接reject。
若是是數組,則依次resolve傳入的thenable對象並在then中註冊回調,設Promise.race()返回的新Promise的對象爲p的話,最快執行完成的entry進入then回調,執行resolve或reject,以此來改變新對象p的狀態。以後的entry完成在此執行resolve或reject均無效,由於Promise狀態一旦肯定沒法改變,詳見上篇關於fulfill()和reject()的註釋和分析。segmentfault

Promise.all()

基本介紹可見阮一峯老師的ES6書籍。
Promise.all方法用於將多個 Promise 實例,包裝成一個新的 Promise 實例。數組

const p = Promise.all([p1, p2, p3]);

p的狀態由p一、p二、p3決定,分紅兩種狀況。
(1)只有p一、p二、p3的狀態都變成fulfilled,p的狀態纔會變成fulfilled,此時p一、p二、p3的返回值組成一個數組,傳遞給p的回調函數。
(2)只要p一、p二、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。
源碼:promise

export default function all(entries) {
  return new Enumerator(this, entries).promise;//這裏返回了一個新對象Promise
}

這裏注意返回了一個新的Promise對象.
Enumerator源碼以下異步

export default class Enumerator {
  constructor (Constructor, input) {
    this._instanceConstructor = Constructor;
    //Promise.all([...])會返回一個新的promise
    this.promise = new Constructor(noop);

    if (!this.promise[PROMISE_ID]) {
      makePromise(this.promise);
    }

    if (isArray(input)) {
      this.length = input.length;
      this._remaining = input.length;//未完成的promise總數量

      this._result = new Array(this.length);//每一個promise結果

      if (this.length === 0) {
        fulfill(this.promise, this._result);
      } else {
        this.length = this.length || 0;
        this._enumerate(input);//處理輸入的數組
        if (this._remaining === 0) {//都執行完畢
          fulfill(this.promise, this._result);
        }
      }
    } else {
      reject(this.promise, validationError());//傳入不是array, reject it
    }
  }

  _enumerate (input) {
    for (let i = 0; this._state === PENDING && i < input.length; i++) {//Enumerator _state?? TODO
      this._eachEntry(input[i], i);
    }
  }

  //處理全部的輸入
  _eachEntry (entry, i) {
    let c = this._instanceConstructor;//Promise
    let {resolve} = c;//Promise.resolve

    if (resolve === originalResolve) {
      let then = getThen(entry);//獲取then方法

      if (then === originalThen &&
        entry._state !== PENDING) {//若是entry已完成或已拒絕
        this._settledAt(entry._state, i, entry._result);
      } else if (typeof then !== 'function') {
        this._remaining--;//不是thenable 直接完成該entry
        this._result[i] = entry;
      } else if (c === Promise) {//不是promise可是一個thenable
        let promise = new c(noop);
        handleMaybeThenable(promise, entry, then);
        this._willSettleAt(promise, i);//暫時狀態不肯定,訂閱之
      } else {
        this._willSettleAt(new c(resolve => resolve(entry)), i);
      }
    } else {
      this._willSettleAt(resolve(entry), i);
    }
  }

  _settledAt (state, i, value) {
    let {promise} = this;

    if (promise._state === PENDING) {
      this._remaining--;//該entry狀態已肯定,待完成總數減一

      if (state === REJECTED) {
        reject(promise, value);//若是傳入entry列表有一個rejected,當即設置promise結果rejected
      } else {
        this._result[i] = value;
      }
    }

    if (this._remaining === 0) {//所有處理完成fulfill
      fulfill(promise, this._result);
    }
  }

  _willSettleAt (promise, i) {
    let enumerator = this;
    //暫時狀態不定,訂閱之
    subscribe(
      promise, undefined,
      value => enumerator._settledAt(FULFILLED, i, value),//回調,設置promise狀態
      reason => enumerator._settledAt(REJECTED, i, reason)
    );
  }
};

代碼不是不少,在此就不逐個方法貼了。
首先看Constructor,細節不表,若是傳入了一個thenable數組會在_enumerate方法中經過_eachEntry挨個處理,細節見註釋。
整體思路就是對傳入列表的元素挨個處理,該resolve則resolve,同時經過_remaining 對未完成的entry進行計數。
若entry是pending狀態,則經過_willSettleAt來訂閱,有肯定結果時進行 _settledAt;
若entry已完成,直接_settledAt肯定結果;
當_remaining === 0;也就是列表全部entry均已有結果,設置Promise.all()返回的新Promise對象的狀態。
要注意,若是有一個entry被reject了,會直接設置 新Promise對象的狀態爲rejected。函數

總結

enter image description here
該圖對Promise的流程總結。
總的來講,Promise經過鏈式語法使得異步操做更加的直觀,避免了回調地獄的出現。使得代碼更加易讀可維護。
細節可見源碼上的註釋,所有代碼可見es6-promise學習筆記oop

閒話

明後兩天公司集體出遊,沒有大多的時間來打磨,但是本身又定了一個每週一篇學習總結小文章的目標,因此擠些時間提早寫完這篇文章來完成目標吧。繼續加油吧!學習

相關文章
相關標籤/搜索