Promise A+規(guī)范解析
什么是 Promise?
Promise 是用于處理異步操作的一種機(jī)制。它表示一個(gè)異步操作的最終結(jié)果。我們主要通過其 then 方法來與 Promise 進(jìn)行交互,通過注冊回調(diào)函數(shù)來獲取 Promise 的最終值,或者得知為什么 Promise 無法被執(zhí)行的原因。
Promise A+ 規(guī)范的三個(gè)要求
Promise A+ 規(guī)范是 JavaScript 中管理異步操作的一種標(biāo)準(zhǔn),它定義了一個(gè)符合特定要求的 Promise API。以下是 Promise A+ 規(guī)范中的三個(gè)主要要求:
-
Promise 狀態(tài):
- Promise 必須處于以下三種狀態(tài)之一:pending(等待態(tài))、fulfilled(完成態(tài))、rejected(拒絕態(tài))。
- 當(dāng)處于 pending 狀態(tài)時(shí),可以轉(zhuǎn)移到 fulfilled 或 rejected 狀態(tài),但一旦轉(zhuǎn)移到其中一種狀態(tài),就不能再改變狀態(tài)。
-
then 方法:
- Promise 對(duì)象必須提供一個(gè)
then方法以訪問其當(dāng)前值、最終值或拒絕原因。 -
then方法接受兩個(gè)參數(shù):onFulfilled和onRejected,分別是當(dāng) Promise 狀態(tài)變?yōu)?fulfilled 或 rejected 時(shí)執(zhí)行的回調(diào)函數(shù)。 -
then方法必須返回一個(gè)新的 Promise 對(duì)象。
- Promise 對(duì)象必須提供一個(gè)
-
Promise 解決程序:
- Promise 解決程序是 Promise A+ 規(guī)范的核心算法,它定義了 Promise 對(duì)象如何處理異步操作和狀態(tài)轉(zhuǎn)換。
- 在 Promise 解決程序中,如果一個(gè) Promise 對(duì)象被 resolved(fulfilled 或 rejected),則必須按照特定的順序執(zhí)行相應(yīng)的回調(diào)函數(shù)隊(duì)列。
- Promise 解決程序確保異步操作的結(jié)果能夠正確傳遞給相應(yīng)的回調(diào)函數(shù),并且保持了回調(diào)函數(shù)的執(zhí)行順序。
遵循這三個(gè)要求可以確保 Promise 對(duì)象的行為符合預(yù)期,并且能夠正確處理異步操作。
ES6 的 Promise 實(shí)現(xiàn)遵循了 Promise A+ 規(guī)范。
ES6 Promise 源碼解析
Promise
function Promise(fn) {
// 判斷是否通過 new 關(guān)鍵字調(diào)用該函數(shù)
if (typeof this !== 'object') {
throw new TypeError('Promises must be constructed via new');
}
// 判斷傳入的參數(shù)是否為函數(shù)
if (typeof fn !== 'function') {
throw new TypeError('Promise constructor\'s argument is not a function');
}
// 在代碼中定義了多個(gè)成員變量,這些變量均以下劃線 "_" 開頭,并且在構(gòu)建時(shí)會(huì)被簡化為 _{隨機(jī)數(shù)} 的格式,以達(dá)到混淆和不鼓勵(lì)直接訪問的目的。
// 選擇這種方式而不是使用 Symbol 或 Object.defineProperty 完全隱藏它們,是出于性能方面的考慮,因?yàn)橥耆[藏可能會(huì)對(duì)性能產(chǎn)生影響。
// _deferreds個(gè)數(shù)標(biāo)識(shí),0為0個(gè),1為一個(gè),2為多個(gè)。
this._deferredState = 0;
// _state有0、1、2、3四個(gè)值。
// 0 代表pending(等待態(tài))
// 1 代表fulfilled(完成態(tài))
// 2 代表 rejected(拒絕態(tài))
// 3 代表采用了另外一個(gè)promise的狀態(tài)
this._state = 0;
// 結(jié)果值
this._value = null;
// 采用了該promise狀態(tài)的promise數(shù)組
this._deferreds = null;
// 在then方法中,會(huì)產(chǎn)生一個(gè)新的Promise對(duì)象,但新產(chǎn)生的對(duì)象不需要執(zhí)行fn,即不需要做doResolve,這里判斷直接返回即可。
if (fn === noop) return;
doResolve(fn, this);
}
Promise._onHandle = null;
Promise._onReject = null;
Promise._noop = noop;
// then 方法,上文提到的Promise A+規(guī)范的三個(gè)要求之一
Promise.prototype.then = function(onFulfilled, onRejected) {
// 調(diào)用對(duì)象判斷,如通過Promise.prototype.then.call(otherPromise, callback)等方式調(diào)用,需做安全調(diào)用處理。
if (this.constructor !== Promise) {
return safeThen(this, onFulfilled, onRejected);
}
// 創(chuàng)建一個(gè)新的Promise對(duì)象,#重點(diǎn)#then或catch都會(huì)產(chǎn)生一個(gè)新的Promise對(duì)象。
var res = new Promise(noop);
handle(this, new Handler(onFulfilled, onRejected, res));
// #重點(diǎn)# 每調(diào)用一次then返回,返回的是一個(gè)新的Promise實(shí)例。
return res;
};
safeThen
// 如上所述,每次調(diào)用 then 方法都會(huì)返回一個(gè)新的 Promise 對(duì)象。
// 然而,在按照 Promise A+ 規(guī)范實(shí)現(xiàn)的 Promise 中,直接返回 ES6 Promise 的實(shí)例是不合理的。
// 因此需要采取以下處理措施:
function safeThen(self, onFulfilled, onRejected) {
// 通過其構(gòu)造器生成一個(gè)新對(duì)象
return new self.constructor(function (resolve, reject) {
// 生成一個(gè)ES6 Promise對(duì)象
var res = new Promise(noop);
// then后調(diào)用回調(diào)
res.then(resolve, reject);
handle(self, new Handler(onFulfilled, onRejected, res));
});
}
doResolve
function doResolve(fn, promise) {
// `done`標(biāo)識(shí),確保onFulfilled(完成態(tài)回調(diào))和onRejected(拒絕態(tài)回調(diào))只會(huì)被調(diào)用一次
var done = false;
var res = tryCallTwo(fn, function (value) {
if (done) return;
done = true;
resolve(promise, value);
}, function (reason) {
if (done) return;
done = true;
reject(promise, reason);
});
// 當(dāng)`done`為`false`,并且發(fā)生異常時(shí),執(zhí)行`reject`。
// 這種情況發(fā)生在Promise的入?yún)fn`在執(zhí)行的時(shí)候發(fā)生異常。
if (!done && res === IS_ERROR) {
done = true;
reject(promise, LAST_ERROR);
}
}
resolve
function resolve(self, newValue) {
// 邊界場景處理,promise不能resolve自身。
if (newValue === self) {
return reject(
self,
new TypeError('A promise cannot be resolved with itself.')
);
}
// 如果newValue是一個(gè)對(duì)象或者函數(shù),需判斷其是否包含then方法。
if (
newValue &&
(typeof newValue === 'object' || typeof newValue === 'function')
) {
var then = getThen(newValue);
// 訪問then方法異常處理
if (then === IS_ERROR) {
return reject(self, LAST_ERROR);
}
// 同根生的then時(shí),且new Value為ES6 Promise對(duì)象時(shí)
if (
then === self.then &&
newValue instanceof Promise
) {
// 跟隨狀態(tài)(俺也一樣)
self._state = 3;
// 新值賦給_value
self._value = newValue;
finale(self);
return;
} else if (typeof then === 'function') {
// 如果有then方法,從新執(zhí)行doResolve(不過是從頭再來)
doResolve(then.bind(newValue), self);
return;
}
}
// 狀態(tài)變?yōu)橥瓿蓱B(tài),newValue賦值給_value
self._state = 1;
self._value = newValue;
finale(self);
}
function reject(self, newValue) {
// 狀態(tài)改為拒絕態(tài),newValue賦值給_value
self._state = 2;
self._value = newValue;
if (Promise._onReject) {
Promise._onReject(self, newValue);
}
finale(self);
}
function finale(self) {
// 調(diào)用handle,根據(jù)_deferredState判斷_deferreds的個(gè)數(shù),采用直接或遍歷的調(diào)用方式,調(diào)用完清空_deferreds
if (self._deferredState === 1) {
handle(self, self._deferreds);
self._deferreds = null;
}
if (self._deferredState === 2) {
for (var i = 0; i < self._deferreds.length; i++) {
handle(self, self._deferreds[i]);
}
self._deferreds = null;
}
}
handle
function handle(self, deferred) {
// 找到_state不等于3的promise(who is your daddy)
while (self._state === 3) {
self = self._value;
}
if (Promise._onHandle) {
Promise._onHandle(self);
}
// 當(dāng)當(dāng)前對(duì)象是pending狀態(tài),將deferred賦值給對(duì)象的_deferreds。如果是多個(gè)deferred,則已數(shù)組元素的方式追加到_deferreds后。
if (self._state === 0) {
if (self._deferredState === 0) {
self._deferredState = 1;
self._deferreds = deferred;
return;
}
if (self._deferredState === 1) {
self._deferredState = 2;
self._deferreds = [self._deferreds, deferred];
return;
}
self._deferreds.push(deferred);
return;
}
handleResolved(self, deferred);
}
function handleResolved(self, deferred) {
// aspa[https://github.com/kriskowal/asap],不展開說,簡單理解就是微任務(wù)。
asap(function() {
// 根據(jù)狀態(tài)判斷需要執(zhí)行的回調(diào)
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
// 沒有回調(diào)的情況
if (cb === null) {
if (self._state === 1) {
resolve(deferred.promise, self._value);
} else {
reject(deferred.promise, self._value);
}
return;
}
// 執(zhí)行回調(diào)
var ret = tryCallOne(cb, self._value);
if (ret === IS_ERROR) {
reject(deferred.promise, LAST_ERROR);
} else {
resolve(deferred.promise, ret);
}
});
}
// Hanlder對(duì)象
function Handler(onFulfilled, onRejected, promise){
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
this.onRejected = typeof onRejected === 'function' ? onRejected : null;
this.promise = promise;
}
getThen tryCallOne tryCallTow
這幾個(gè)函數(shù)是為了避免在關(guān)鍵函數(shù)中使用try/catch,把它們都提取到這里。
// 記錄最后一次異常
var LAST_ERROR = null;
// 異常標(biāo)識(shí),使用`{}`作為值的作用類似于`Symbol`
var IS_ERROR = {};
function getThen(obj) {
try {
return obj.then;
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
function tryCallOne(fn, a) {
try {
return fn(a);
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
function tryCallTwo(fn, a, b) {
try {
fn(a, b);
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
以上是對(duì)Promise core.js的解析。后面會(huì)解析ES6 Promise的其它特性的實(shí)現(xiàn)。