本篇爲上一篇源碼學習(1)的補充,主要是來介紹Promise.all()和Promise.race()方法。
閒話少敘,進入正題git
首先來簡單介紹一下功能吧,詳細好比可見阮一峯老師的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
基本介紹可見阮一峯老師的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。函數
該圖對Promise的流程總結。
總的來講,Promise經過鏈式語法使得異步操做更加的直觀,避免了回調地獄的出現。使得代碼更加易讀可維護。
細節可見源碼上的註釋,所有代碼可見es6-promise學習筆記oop
明後兩天公司集體出遊,沒有大多的時間來打磨,但是本身又定了一個每週一篇學習總結小文章的目標,因此擠些時間提早寫完這篇文章來完成目標吧。繼續加油吧!學習