做者: HerryLojavascript
本文永久有效連接: https://github.com/AttemptWeb......java
Promises
對象被用於表示一個異步操做的最終完成 (或失敗), 及其結果值。主要是爲了解決異步操做的問題。git
一個 Promise對象有如下三種狀態:es6
pending: 初始狀態,既不是成功,也不是失敗狀態。 fulfilled(resolved): 意味着操做成功完成。 rejected: 意味着操做失敗。
Promise對象內部運行的一個變化, 變化以下:github
1. 當new Promise()被實例化後,即表示Promise 進入pending初始化狀態,準備就緒,等待運行。 2. 一旦Promise實例運行成功或者失敗以後,實例狀態就會變爲fulfilled 或者 rejected,此時狀態就沒法變動。
任何系統或函數均可以簡化爲輸入輸出系統,數據輸入 ——> 黑箱 ——> 輸出
,以下圖:npm
咱們能夠拿上圖來類比Promise函數
,代碼以下:c#
// 實例化 Promise new Promise((resolve, reject)=> { // 輸入 AjaxRequest.post({ url: 'url', data: {}, sueccess: ()=> { // resolve resolve(res) }, fail: (err)=> { // reject reject(err) } }) }).then((res)=> { // res 輸出 // ...操做 }).catch((err)=> { // err 輸出 // ...操做 })
在上面的代碼中,Promise函數參數能夠做爲
輸入信息,然後通過Promise的內部處理(黑箱
),在then函數或者catch函數參數中
輸出信息,這是一個完整的系統(別被它分散了注意力,這個解釋的目的:讓你更加關注Promise函數內部實現)。下面咱們將解析Promise中黑箱操做。數組
Promise函數實例化,會先進入到pending狀態,在這個狀態下,它會運行以下函數:promise
你能夠直接查看源碼:Promise函數:54行,對照閱讀,同時,在下面的代碼中我會作沒必要要的省略。微信
// 首先運行,Promise構造函數 function Promise(fn) { // ...省略檢驗 // _deferreds的類型,1是 single,2是 array this._deferredState = 0; // 0 - pending // 1 - fulfilled(resolved) // 2 - rejected // 3 - 另外一個Promise的狀態 this._state = 0; // promise 執行結果 this._value = null; // then註冊回調數組 this._deferreds = null; // fn等於noop 即return if (fn === noop) return; // 接受Promise回調函數 和 this 做爲參數 doResolve(fn, this); }
Promise
構造函數,會初始化屬性,其中參數fn
就是咱們傳入的函數。其中doResolve
函數接受Promise函數參數
和 this
做爲參數,this指向它本身,負責執行fn函數。等下面的then
函數和catch
函數的回調函數註冊完以後,doResolve
函數將當即執行。
能夠查看代碼,查看源碼:then函數:72行。then方法的回調函數會被存儲在this._deferreds
中。仔細閱讀代碼中的備註
Promise.prototype.then = function(onFulfilled, onRejected) { if (this.constructor !== Promise) { // safeThen函數也是經過調用handle函數,return 新的Promise對象 return safeThen(this, onFulfilled, onRejected); } // 生成新的Promise對象 var res = new Promise(noop); handle(this, new Handler(onFulfilled, onRejected, res)); return res; }; // Handler構造函數 // 它的做用是掛載 then中的回調函數 和 一個空的Promise對象 function Handler(onFulfilled, onRejected, promise){ // then中的Fulfilled回調函數 this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; // then中的Rejected回調函數 this.onRejected = typeof onRejected === 'function' ? onRejected : null; // 保存新的Promise this.promise = promise; }
// 保存then註冊回調函數,更新回調函數狀態 function handle(self, deferred) { // 。。。省略 // pedding 狀態 if (self._state === 0) { // deferred == new Handler(onFulfilled, onRejected, res) if (self._deferredState === 0) { self._deferredState = 1; // 存儲then回調deferred對象 self._deferreds = deferred; return; } if (self._deferredState === 1) { self._deferredState = 2; // 存儲then回調deferred對象 self._deferreds = [self._deferreds, deferred]; return; } // 存儲then回調函數對象 self._deferreds.push(deferred); return; } // 只有當進入到非pedding狀態,handleResolved纔會運行 handleResolved(self, deferred); }
Handler
函數生成一個deffer對象,用於保存then函數中的onFulfilled和onRejected回調,以及返回的新的promise實例
then
方法中的核心函數就是handle
函數,它負責接收this
和new Handler
對象。若在pedding
狀態下,handle
函數只負責註冊回調函數,更新回調函數狀態。在非pedding
狀態下,則會執行handleResolved
函數。
Promise.prototype['catch'] = function (onRejected) { return this.then(null, onRejected); };
catch
方法的回調函數實際是經過then
方法來完成保存的。
負責運行Promise實例對象中的回調函數參數fn。
// 調用doResolve函數 function doResolve(fn, promise) { var done = false; // tryCallTwo函數執行 相似於 // (resolve, reject) => {if(err){reject(err);return};resolve(res)}執行; var res = tryCallTwo(fn, function (value) { if (done) return; done = true; resolve(promise, value); }, function (reason) { if (done) return; done = true; reject(promise, reason); }); // fn函數調用失敗,手動運行reject函數 if (!done && res === IS_ERROR) { done = true; reject(promise, LAST_ERROR); } }
doResolve
是同步直接調用傳入的函數。其中tryCallTwo
函數做用是調用函數fn
,它接受三個參數。先執行fn函數,根據結果,再執行resolve
函數或reject
函數。在resolve
函數或reject
函數被調用以前,Promise對象的狀態依然是pending
。
pending狀態下函數調用基本流程以下:
當初始化完以後,fn函數執行完成,接下來就會運行resolve
函數或者reject
函數。
若Promise對象的fn函數執行正常,以後就會調用resolve函數。能夠查看源碼:resolve函數:131行。
function resolve(self, newValue) { // 。。。省略 // newValue存在 & (newValue是一個對象 || newValue是一個函數) if ( newValue && (typeof newValue === 'object' || typeof newValue === 'function') ) { // 獲取then函數 var then = getThen(newValue); // 。。。省略 if ( then === self.then && newValue instanceof Promise ) { // 若是newValue 是一個Promise對象,那麼調用finale函數 self._state = 3; self._value = newValue; finale(self); return; } else if (typeof then === 'function') { // 若是newValue 是一個函數,就繼續調用doResolve函數 doResolve(then.bind(newValue), self); return; } } // 標記完成,進入結束流程 self._state = 1; self._value = newValue; finale(self); }
確認newValue
的值,若是newValue是一個函數,就繼續循環調用doResolve
函數;若是newValue 是一個Promise對象,那麼就直接調用finale
函數。都不是,則直接調用finale
函數。
進入結束流程,finale結束。
function finale(self) { // 單個回調 if (self._deferredState === 1) { // 執行handle函數,實際是執行handleResolved handle(self, self._deferreds); self._deferreds = null; } // 回調數組 if (self._deferredState === 2) { for (var i = 0; i < self._deferreds.length; i++) { // 執行handle函數,實際是執行handleResolved handle(self, self._deferreds[i]); } self._deferreds = null; } }
finale
函數表示進入結束流程,執行handle
函數。同時在上面已經說到,在非pedding
狀態下,執行handle
函數,實際會是執行handleResolved
函數。
handleResolved
負責收尾工做,負責執行then或者catch方法註冊的回調函數。仔細閱讀代碼中的備註
var asap = require('asap/raw'); function handleResolved(self, deferred) { asap(function() { var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; // 不存在 onFulfilled & onRejected // deferred.promise 只是一個空的Promise對象 if (cb === null) { // 1 - fulfilled(resolved) if (self._state === 1) { resolve(deferred.promise, self._value); } else { reject(deferred.promise, self._value); } return; } // 執行cb回調函數 var ret = tryCallOne(cb, self._value); if (ret === IS_ERROR) { // 錯誤,報reject reject(deferred.promise, LAST_ERROR); } else { resolve(deferred.promise, ret); } }); }
經過異步asap
調用,若不存在onFulfilled
和onRejected
,直接調用resolve
或reject
。若存在,則tryCallOne
回調的結果,直接調用resolve
或reject
。其中的deferred
就是上文提到的new Handler
實例對象。真正會影響最後這步流程的,實際上是deferred.onFulfilled
或者 deferred.onRejected
的回調執行,執行完回調後,這個Promise的執行過程就基本完成。
而reject
函數在這裏我就不說了,有興趣的能夠看查看源碼:reject函數
Promise對象調用函數的基本流程圖,只是一個大體的走向,便於理解:
ps: 微信公衆號:Yopai,有興趣的能夠關注,每週不按期更新,分享能夠增長世界的快樂