做者: HerryLohtml
本文永久有效連接: https://github.com/AttemptWeb......git
Promises
對象被用於表示一個異步操做的最終完成 (或失敗), 及其結果值。主要是爲了解決異步操做的問題。es6
一個 Promise對象有如下三種狀態:github
pending: 初始狀態,既不是成功,也不是失敗狀態。
fulfilled(resolved): 意味着操做成功完成。
rejected: 意味着操做失敗。
複製代碼
Promise對象內部運行的一個變化, 變化以下:npm
1. 當new Promise()被實例化後,即表示Promise 進入pending初始化狀態,準備就緒,等待運行。
2. 一旦Promise實例運行成功或者失敗以後,實例狀態就會變爲fulfilled 或者 rejected,此時狀態就沒法變動。
複製代碼
任何系統或函數均可以簡化爲輸入輸出系統,數據輸入 ——> 黑箱 ——> 輸出
,以下圖:c#
咱們能夠拿上圖來類比Promise函數
,代碼以下:數組
// 實例化 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
Promise函數實例化,會先進入到pending狀態,在這個狀態下,它會運行以下函數:bash
你能夠直接查看源碼: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行
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,有興趣的能夠關注,每週不按期更新,分享能夠增長世界的快樂