前言
then/promise
專案是基於Promises/A+
標準實現的Promise
庫,從這個專案當中,我們來看Promise
的原理是什麼,它是如何做到的,從而更加熟悉Promise
分析
從index.js當中知道,它是先引出了./core.js
,隨後各自執行了其他檔案的程式碼,通過requeire
的方法。
我們首先先想一下最基礎的promise用法
new Promise((resolve, reject) => {
resolve(4);
}).then(res => {
console.log(res); // export 4
});
Promise中的標準
標準中規定:
- Promise物件初始狀態為
Pending
,在被resolve
或reject
時,狀態變為Fulfilled
或Rejected
-
resolve
接收成功的資料,reject
接收失敗或錯誤的資料 -
Promise
物件必須有一個then
方法,且只接受兩個可函式引數onFulfilled
、onRejected
index.js
`use strict`;
module.exports = require(`./core.js`);
require(`./done.js`);
require(`./finally.js`);
require(`./es6-extensions.js`);
require(`./node-extensions.js`);
require(`./synchronous.js`);
我們先看src/core.js
function Promise(fn) {
// 判斷 this一定得是object不然就會報錯,這個方法一定得要new出來
if (typeof this !== `object`) {
throw new TypeError(`Promises must be constructed via new`);
}
// 判斷fn 一定得是一個函式
if (typeof fn !== `function`) {
throw new TypeError(`Promise constructor`s argument is not a function`);
}
this._deferredState = 0;
this._state = 0;
this._value = null;
this._deferreds = null;
if (fn === noop) return;
// 最終doResolve很關鍵
doResolve(fn, this);
}
Promise
是一個構造方法,開始時,它進行了校驗,確保了fn
是一個函式,隨後對一些變數進行了初始化,最後執行了doResolve()
我們接著看doResolve
這個方法。
/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/
//
// 確保`onFulfilled`和`onRejected`方法只呼叫一次
// 不保證非同步
function doResolve(fn, promise) {
var done = false;
var res = tryCallTwo(fn, function (value) {
// 如果done 為true 則return
if (done) return;
done = true;
// 回撥執行 resolve()
resolve(promise, value);
}, function (reason) {
// 如果done 為true 則return
if (done) return;
done = true;
reject(promise, reason);
});
// res為truCallTwo()的返回值
// 如果done沒有完成 並且 res 是 `IS_ERROR`的情況下
// 也會執行reject(),同時讓done完成
if (!done && res === IS_ERROR) {
done = true;
reject(promise, LAST_ERROR);
}
}
doResolve
最關鍵的是執行了tryCallTwo
方法,這個方法的第二,第三個引數都是回撥,當執行回撥後,done
為true,同時各自會執行resolve()
或者reject()
方法。最後當tryCallTwo
的返回值為IS_ERROR
時,也會執行reject()
方法。
我們先來看一下tryCallTwo
方法
function tryCallTwo(fn, a, b) {
try {
fn(a, b);
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
fn
實際就是Promise
初始化時的匿名函式(resolve, reject) => {}
,a
,b
則代表的是resolve()
和reject()
方法,當我們正常執行完promise
函式時,則執行的是resolve
則在doResolve中
,我們當時執行的第二個引數被回撥,如果報錯,reject()
被執行,則第二個引數被回撥。最後捕獲了異常,當發生了報錯時,會return IS_ERROR
,非報錯時會return undinfed
再回到剛才的doResolve
方法,當執行了第二個引數的回撥之後,會執行resolve
方法
function resolve(self, newValue) {
// Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
// 不能吃傳遞自己
if (newValue === self) {
// 報錯
return reject(
self,
new TypeError(`A promise cannot be resolved with itself.`)
);
}
// promise作為引數
if (
newValue &&
(typeof newValue === `object` || typeof newValue === `function`)
) {
// 獲取它的promise方法 讀取newValue.then
var then = getThen(newValue);
if (then === IS_ERROR) {
// 如果then IS_ERROR
return reject(self, LAST_ERROR);
}
if (
// 如果then是self的then
// 並且Promise
then === self.then &&
// newValue 屬於Promise
newValue instanceof Promise
) {
// _state為3
// 一般then之後走這裡
// 執行then(newValue)返回了promise
self._state = 3;
// selft.value為newValue
self._value = newValue;
// 當state為3時執行 finale
finale(self);
return;
} else if (typeof then === `function`) {
doResolve(then.bind(newValue), self);
return;
}
}
self._state = 1;
self._value = newValue;
finale(self);
}
在沒有鏈式呼叫then
的情況下(也就是隻要一個then
)的情況下,會將內部狀態_state
設定成3
,將傳入值賦給內部變數_value
最後會執行final()
方法,不然則會使用doResolve
來呼叫then
我們再來看下reject
function reject(self, newValue) {
// _state = 2為reject
self._state = 2;
self._value = newValue;
if (Promise._onReject) {
Promise._onReject(self, newValue);
}
finale(self);
}
在reject
當中我們的_state
變更為了2,同樣最後finale
被呼叫。
我們來看下finale
函式
// 執行自己的deferreds
function finale(self) {
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
handle(self, self._deferreds[i]);
}
// 將deferred 置空
self._deferreds = null;
}
}
在該方法當中根據不同的_deferredState
,會執行不同的handle
方法。
我們再來看handle
方法
function handle(self, deferred) {
while (self._state === 3) {
self = self._value;
}
// 如果有onHandle方法 則執行該方法
if (Promise._onHandle) {
Promise._onHandle(self);
}
// (初始 _state 為0)
if (self._state === 0) {
// (初始 _deferredState 為0)
if (self._deferredState === 0) {
self._deferredState = 1;
self._deferreds = deferred;
return;
}
// 如果 _deferredState是1 則__deferreds是一個陣列
if (self._deferredState === 1) {
self._deferredState = 2;
self._deferreds = [self._deferreds, deferred];
return;
}
// 當走到這裡 _deferredState應該是2 將deferred
// 插入到陣列當中
self._deferreds.push(deferred);
return;
}
handleResolved(self, deferred);
}
這裡比較關鍵的應該就是通過deferredState
不同的狀態,將deferred
放入deferreds
當中。另外當我們的_state
不為0
時,最終會執行handleResolved
。
繼續看handleResolve()
方法
function handleResolved(self, deferred) {
asap(function() {
// _state為1時,cb = onFulfilled 否則 cb = onRejected
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
if (cb === null) {
if (self._state === 1) {
resolve(deferred.promise, self._value);
} else {
reject(deferred.promise, self._value);
}
return;
}
var ret = tryCallOne(cb, self._value);
if (ret === IS_ERROR) {
reject(deferred.promise, LAST_ERROR);
} else {
resolve(deferred.promise, ret);
}
});
}.then((res) => {
}).catch((error) => {
})
在這個方法當中,會根據我們任務(_state)的不同狀態,來執行onFulfilled
或者onRejected
方法。當此方法呼叫時,也就是我們一個簡單的Promise
的結束。
回到剛才說的Promise
構造方法結束的時候
設定了Promise
函式的一些變數
Promise._onHandle = null;
Promise._onReject = null;
Promise._noop = noop;
隨後在Promise
的原型上設定了then
方法。
Promise.prototype.then = function(onFulfilled, onRejected) {
// 首先看這是誰構造的 如果不是promise
// 則return 執行safeThen
if (this.constructor !== Promise) {
return safeThen(this, onFulfilled, onRejected);
}
// 如果是則初始化一個Promise 但是引數 noop 為空物件 {}
var res = new Promise(noop);
// 隨後執行handle方法
handle(this, new Handler(onFulfilled, onRejected, res));
return res;
};
在then
這個方法中首先判斷了它是否由Promise
構造的,如果不是,則返回並執行safeThen
,不然則執行Promise
構造一個res
物件,然後執行handle
方法,最後將promise
變數res
返回。handle
方法之前有提過,在這裡,當初始化時_state
和_deferred
的轉改都為0
,因此它會將defrred
儲存到promise
當中。
先看一下上面說的safeThen
方法
function safeThen(self, onFulfilled, onRejected) {
return new self.constructor(function (resolve, reject) {
var res = new Promise(noop);
res.then(resolve, reject);
handle(self, new Handler(onFulfilled, onRejected, res));
});
}
流程
需要有一個Promise
的構造方法,這個構造方法最終會執行它的引數(resolve, reject) => {}
,宣告的then
方法會通過handle()
方法將onFulfilled
和onRejected
方法儲存起來。當在外部呼叫resolve
或者onRejected
時,最終也會執行handle
但是它,會最後根據狀態來執行onFulfilled
或者onRejected
。從而到我們的then
回撥中。
Promise的擴充套件
done
對done
的擴充套件在src/done.js
當中
`use strict`;
var Promise = require(`./core.js`);
module.exports = Promise;
Promise.prototype.done = function (onFulfilled, onRejected) {
var self = arguments.length ? this.then.apply(this, arguments) : this;
self.then(null, function (err) {
setTimeout(function () {
throw err;
}, 0);
});
};
內部執行了then()
finally
對finally
的擴充套件在src/finally.js
當中
在Promise
的標準當中,本身是沒有finally
方法的,但是在ES2018
的標準裡有,finally
的實現如下
`use strict`;
var Promise = require(`./core.js`);
module.exports = Promise;
Promise.prototype.finally = function (callback) {
return this.then(function (value) {
return Promise.resolve(callback()).then(function () {
return value;
});
}, function (err) {
return Promise.resolve(callback()).then(function () {
throw err;
});
});
};
Promise
的onFulfilled
和onRejected
不管回撥的哪個,最終都會觸發callback
回撥。還要注意的一點是finally
的返回也是一個Promise
。
es6-extensions.js
在es6-extensions.js
檔案當中包含了ES6的一些擴充套件。
Promise.resolve
function valuePromise(value) {
var p = new Promise(Promise._noop);
// 將_state賦值為 非0
// _value進行儲存
p._state = 1;
p._value = value;
// 這樣做的目的是省略的一些前面的邏輯
return p;
}
Promise.resolve = function (value) {
if (value instanceof Promise) return value;
if (value === null) return NULL;
if (value === undefined) return UNDEFINED;
if (value === true) return TRUE;
if (value === false) return FALSE;
if (value === 0) return ZERO;
if (value === ``) return EMPTYSTRING;
// value return new Promise
if (typeof value === `object` || typeof value === `function`) {
try {
var then = value.then;
if (typeof then === `function`) {
// 返回 返回了一個新的Promise物件
return new Promise(then.bind(value));
}
} catch (ex) {
// 如果報錯 則返回一個就只
return new Promise(function (resolve, reject) {
reject(ex);
});
}
}
return valuePromise(value);
};
Promise.reject
Promise.reject = function (value) {
return new Promise(function (resolve, reject) {
reject(value);
});
};
Promise.all
Promise.all = function (arr) {
// 類似深拷貝了一份給了args
var args = Array.prototype.slice.call(arr);
return new Promise(function (resolve, reject) {
// 判斷了all的promise數量
if (args.length === 0) return resolve([]);
// remaining則是promise陣列的長度
var remaining = args.length;
// i為index val 為 promise
function res(i, val) {
if (val && (typeof val === `object` || typeof val === `function`)) {
if (val instanceof Promise && val.then === Promise.prototype.then) {
while (val._state === 3) {
val = val._value;
}
if (val._state === 1) return res(i, val._value);
if (val._state === 2) reject(val._value);
// val._state 為 0時 走這裡
val.then(function (val) {
res(i, val);
}, reject);
return;
} else {
var then = val.then;
if (typeof then === `function`) {
var p = new Promise(then.bind(val));
p.then(function (val) {
res(i, val);
}, reject);
return;
}
}
}
args[i] = val;
// 當所有的promise執行完 則是remaining為0
// 則執行resolve();
if (--remaining === 0) {
resolve(args);
}
}
// 遍歷所有的promise
for (var i = 0; i < args.length; i++) {
res(i, args[i]);
}
});
};
Promise.all()
返回的也是一個Promise
函式。
內部有一個remaining
變數每當執行完一個promise
函式後就會減一,當所有promise
執行完,會執行自己的resolve
。
Promise.race
Promise.race = function (values) {
return new Promise(function (resolve, reject) {
values.forEach(function(value){
Promise.resolve(value).then(resolve, reject);
});
});
};
遍歷傳入的promise
陣列,經過Promise.resolve(value)
的原始碼可以看到,如果value
是一個Promise
則戶直接將這個value
返回,最後陣列中的promise
哪個優先回撥即執行。
Promise.property.catch
catch
在標準當中也是沒有,雖然我們用的比較多
Promise.prototype[`catch`] = function (onRejected) {
return this.then(null, onRejected);
};
catch
的回撥實際是then(null, onRejected)
的回撥。
廣而告之
本文釋出於薄荷前端週刊,歡迎Watch & Star ★,轉載請註明出處。