Promise源碼指南
什么是Promise?
相信許多從事前端的小伙伴們都用過Promise,為了解決異步編程的弊端(地獄回調(diào)等問題),ES6提供了一個強大的東西,那就是Promise。Promise是將異步任務(wù)轉(zhuǎn)換為同步任務(wù)的一個構(gòu)造函數(shù),通過resolve,reject改變?nèi)蝿?wù)的狀態(tài),必不可少的then方法用來收Promise的值,這些都是Promise的基本使用。那么Promise是如何處理狀態(tài)的,又是如何實現(xiàn)resove,reject方法的,又是如何實現(xiàn)鏈?zhǔn)秸{(diào)用的呢,如果你不知道,那么這篇文章可以幫到你,下面我們一起來解析一下Promise到底是如何實現(xiàn)的,相信看完這篇文章,每個人都可以寫出屬于自己的Promise方法。
這里引入github上的一份符合Promise A+規(guī)范的源碼 https://github.com/then/promise
函數(shù)對象Promise
我們先來看看src/index.js這個文件
一些必要的定義
// 定義空函數(shù)
function noop() {}
// 用來存儲錯誤信息
var IS_ERROR = {}
// 獲取實例的then
function getThen(obj) {
try {
return obj.then;
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
// 執(zhí)行then方法回調(diào)函數(shù)
function tryCallOne(fn, a) {
try {
return fn(a);
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
// 執(zhí)行Promise構(gòu)造函數(shù)
function tryCallTwo(fn, a, b) {
try {
fn(a, b);
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
Promise構(gòu)造函數(shù)
function Promise(fn) {
// 校驗實例
if (typeof this !== 'object') {
throw new TypeError('Promises must be constructed via new');
}
// 校驗Promise的構(gòu)造函數(shù)
if (typeof fn !== 'function') {
throw new TypeError('Promise constructor\'s argument is not a function');
}
// 存儲的實例狀態(tài) 0代表還未存儲 1代表存儲了1個 2代表存儲了2個
this._deferredState = 0;
// Promise的狀態(tài) 0代表padding 1代表Fulfilled 2代表Rejected 3代表resolve傳入的是promise實例
this._state = 0;
// Fulfilled的值
this._value = null;
// 用來存儲調(diào)用then后的實例
this._deferreds = null;
if (fn === noop) return;
// 處理Promise的參數(shù)
doResolve(fn, this);
}
//以下先不做解釋
newPromise._onHandle = null;
newPromise._onReject = null;
newPromise._noop = noop;
當(dāng)使用new操作符實例化Promise的時候,必須傳入Promise的構(gòu)造函數(shù)fn,否則將拋出錯誤
然后初始化實例的狀態(tài)和值
最后調(diào)用doResolve方法
下面我們一起來看一下doResolve這個方法
doResolve方法
function doResolve(fn, promise) {
// done防止重復(fù)觸發(fā)
var done = false;
// tryCallTwo 用于處理并掛載reolve,reject方法
// 傳入3個參數(shù),Promise構(gòu)造函數(shù)本身,resolve回調(diào),reject回調(diào)
var res = tryCallTwo(fn, function (value) {
if (done) return;
done = true;
// 處理resolve方法
resolve(promise, value);
}, function (reason) {
if (done) return;
done = true;
// 處理reject方法
reject(promise, reason);
});
// 報錯則直接reject
if (!done && res === IS_ERROR) {
done = true;
reject(promise, LAST_ERROR);
}
}
doResolve方法接收兩個參數(shù)(Promise的構(gòu)造函數(shù),Promise的實例也就是this)
這里的tryCallTwo扮演了一個重要角色,它執(zhí)行了構(gòu)造函數(shù)fn,傳入三個參數(shù)(fn構(gòu)造函數(shù),resolve回調(diào)函數(shù),reject回調(diào)函數(shù))
我們回過頭來看看tryCallTwo這個函數(shù)
// 執(zhí)行Promise構(gòu)造函數(shù)
function tryCallTwo(fn, a, b) {
//...
fn(a, b);
//...
}
這里的a,b就是Promise的resolve和reject
執(zhí)行fn并傳入a和b
然后resolve和reject又分別執(zhí)行了resolve方法和reject方法
當(dāng)實例調(diào)用了resolve之后就會執(zhí)行resolve方法,下面來看看resove這個方法
resolve方法
function resolve(self, newValue) {
// 防止resolve的值傳入實例本身
if (newValue === self) {
return reject(
self,
new TypeError('A promise cannot be resolved with itself.')
);
}
// 這里的if主要用于處理resolve一個Promise
if (
newValue &&
(typeof newValue === 'object' || typeof newValue === 'function')
) {
// 獲取Promise的then方法
var then = getThen(newValue);
if (then === IS_ERROR) {
return reject(self, LAST_ERROR);
}
// 如果傳入的是Promise的實例
if (
then === self.then &&
newValue instanceof newPromise
) {
self._state = 3;
// 直接把傳入的Promise實例掛載到value
self._value = newValue;
finale(self);
return;
} else if (typeof then === 'function') { //如果傳入帶有then方法
// 把then當(dāng)作構(gòu)造函數(shù)并且把this指向這個then的對象
doResolve(then.bind(newValue), self);
return;
}
}
self._state = 1;
self._value = newValue;
finale(self);
}
resolve方法一開始會先判斷resolve傳入的值,如果resolve傳入的是一個「Promise實例」,將會進行以下處理
將實例的狀態(tài)變?yōu)?
將實例的值變?yōu)閭魅氲腜romise實例
調(diào)用finale方法
如果resolve傳入的是Promise實例并且「包含then方法」則調(diào)用doResolve執(zhí)行這個實例的構(gòu)造函數(shù)
如果resolve傳入的是普通值而不是Promise實例,則做以下處理
將實例的狀態(tài)變?yōu)?
將實例的值變?yōu)閞esolve傳入的值
調(diào)用finale方法
reject方法
function reject(self, newValue) {
// reject的時候狀態(tài)變?yōu)?
self._state = 2;
// 保存錯誤信息到_value
self._value = newValue;
if (newPromise._onReject) {
newPromise._onReject(self, newValue);
}
finale(self);
}
當(dāng)Promise執(zhí)行reject的時候此時狀態(tài)變?yōu)?code style="margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">2
保存reject的錯誤信息到_value調(diào)用finale方法
finale方法
function finale(self) {
// 只調(diào)用一次then
if (self._deferredState === 1) {
handle(self, self._deferreds);
self._deferreds = null;
}
// 調(diào)用多次then
if (self._deferredState === 2) {
// console.log(self._deferreds);
for (var i = 0; i < self._deferreds.length; i++) {
handle(self, self._deferreds[i]);
}
self._deferreds = null;
}
}
finaale方法在這里的作用就相當(dāng)于handle方法的中轉(zhuǎn)站,根據(jù)不同的情況去調(diào)用handle方法
上面有提到過_deferredState是用來記錄存儲的實例狀態(tài)
「同一個Promise對象下」,_deferredState的值是隨著then調(diào)用的次數(shù)決定的,為什么說只有在同一個Promise對象下才會觸發(fā)呢,我們來看下面一個小例子
const promise2 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('哈哈哈')
}, 500);
})
.then(res => {}) //第一次then
.then(res => {}) //第二次then
到這里很多小伙伴會以為代碼會在’if(self._deferredState === 2){}‘這個判斷里面執(zhí)行
事實上這樣去調(diào)用then的情況下,_deferredState的值永遠(yuǎn)只會=1,只會在’if(self._deferredState === 1){}‘的這個判斷里面去執(zhí)行
到這里小伙伴們又會說,不對啊,我這個不是在同一個Promise對象下嗎,我不是只實例化了一次嗎?
這里在使用Promise的時候確實是只實例化了一次,但是每次調(diào)用then方法返回的Promise跟實例的Promise并不是同一個引用,也就是說,這里的self并不是實例出來的對象,后面會詳細(xì)介紹then是怎么返回Promise對象的
promise2.then(res => {
console.log(res);
})
promise2.then(res => {
console.log(res);
})
只有這樣調(diào)用then,才會執(zhí)行’if(self._deferredState === 2){}‘這個判斷
Promise.prototype.then
Promise.prototype.then = function(onFulfilled, onRejected) {
if (this.constructor !== Promise) {
return safeThen(this, onFulfilled, onRejected);
}
// 新建一個promise實例
var res = new Promise(noop);
// // new Handler 構(gòu)建了一個包含新的promise實例和resolve,reject方法的對象
handle(this, new Handler(onFulfilled, onRejected, res));
// 每次then處理完之后返回一個新的promise實例
return res;
};
接收兩個參數(shù)resolve和reject函數(shù)
一開始先判斷實例的構(gòu)造函數(shù)是不是Promise(防止外部修改prototype.constructor)
這里的safeThen函數(shù)不作過多解釋,主要用于實例化外部實例的構(gòu)造函數(shù)并返回實例
創(chuàng)建一個空的Promise實例給res
我們看看下面這句代碼主要做了什么
handle(this, new Handler(onFulfilled, onRejected, res));
我們拆開來看,先看看new Handler實例化得到了什么
// 這個函數(shù)的作用就是每次then的時候通過這個構(gòu)造函數(shù)根據(jù)resolve和reject構(gòu)建一個新的對象
function Handler(onFulfilled, onRejected, promise){
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
this.onRejected = typeof onRejected === 'function' ? onRejected : null;
this.promise = promise;
}
由此可見通過實例化Handler函數(shù)可以得到一個大概長這樣子的對象

所以handle函數(shù)傳入了2個參數(shù),一個是this,一個是像這樣子的對象,那么,接下來就可以看handle函數(shù)做了什么事情了
function handle(self, deferred) {
// resolve傳入的是promise的實例,this(上下文)則改成傳入的promise實例
while (self._state === 3) {
self = self._value;
}
if (newPromise._onHandle) {
newPromise._onHandle(self);
}
// 當(dāng)padding狀態(tài)時(沒有調(diào)用resolve也沒有調(diào)用reject)
// 存儲新的promise實例
if (self._state === 0) {
if (self._deferredState === 0) {
self._deferredState = 1;
self._deferreds = deferred;
return;
}
// 已經(jīng)調(diào)用過1次then
if (self._deferredState === 1) {
self._deferredState = 2;
self._deferreds = [self._deferreds, deferred];
return;
}
// 多次then
self._deferreds.push(deferred);
return;
}
handleResolved(self, deferred);
}
看上面的代碼注釋大家都應(yīng)該知道了,handle函數(shù)主要就是用作修改_deferredState的值和保存每次then生成的新實例
最后return res 每次調(diào)用then方法都返回一個新的Promise實例
鏈?zhǔn)秸{(diào)用
細(xì)心的你可能已經(jīng)發(fā)現(xiàn)了在前面的handle方法里的最下面調(diào)用了一個名為handleResolved的函數(shù),話不多說直接上代碼
function handleResolved(self, deferred) {
// asap(function() {
// 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);
// }
// });
// resolve后,獲取then的回調(diào)
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
// 如果then沒有回調(diào),則手動調(diào)用回調(diào)
if (cb === null) {
if (self._state === 1) {
resolve(deferred.promise, self._value);
} else {
reject(deferred.promise, self._value);
}
return;
}
// 獲取then的返回值
var ret = tryCallOne(cb, self._value);
if (ret === IS_ERROR) {
reject(deferred.promise, LAST_ERROR);
} else {
resolve(deferred.promise, ret);
}
}
其實源碼還引入了asap這個庫,我先把這個asap函數(shù)注釋掉了,理由是懶得用npm,本文全程是html+js+live-server
但是大家千萬不要忽略asap這個函數(shù)!!!
縱觀全文到現(xiàn)在,大家好像并沒有發(fā)現(xiàn)源碼有一點點異步的信息,大家都知道Promise是異步執(zhí)行的,就是靠asap函數(shù),通過setImmediate這個核心方法去異步執(zhí)行asap里面的東西,有興趣的可以去翻翻asap的源碼看下具體是怎么實現(xiàn)的
這里只是為了更好的解析源碼,沒有asap那么Promise就沒有意義了,ok我們回歸正文
這里就很好理解了
通過tryCallOne函數(shù)得到then的返回值
然后再次調(diào)用resolve,如果報錯或者手動調(diào)用reject則調(diào)用reject,這樣就完成了Promise的鏈?zhǔn)秸{(diào)用
擴展
src/es6-extensions.js
定義
var TRUE = valuePromise(true);
var FALSE = valuePromise(false);
var NULL = valuePromise(null);
var UNDEFINED = valuePromise(undefined);
var ZERO = valuePromise(0);
var EMPTYSTRING = valuePromise('');
function valuePromise(value) {
var p = new newPromise(newPromise._noop);
p._state = 1;
p._value = value;
return p;
}
Promise.resolve
Promise.resolve = function (value) {
// 判斷value是否是Promise得實例
if (value instanceof Promise) return value;
// 因為0,'',null等會被隱式轉(zhuǎn)換成false,所以這里做了精確判斷
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;
// 這里的判斷跟之前resolve方法一樣都是判斷傳入的是否是一個Promise
if (typeof value === 'object' || typeof value === 'function') {
try {
var then = value.then;
if (typeof then === 'function') {
return new Promise(then.bind(value));
}
} catch (ex) {
return new Promise(function (resolve, reject) {
reject(ex);
});
}
}
// 根據(jù)valuePromise方法返回一個新的Promise
return valuePromise(value);
};
try/catch 用于捕獲Promise.resolve傳入的val是否包含then方法
Promise.all
Promise.all = function (arr) {
var args = iterableToArray(arr);
return new Promise(function (resolve, reject) {
if (args.length === 0) return resolve([]);
var remaining = args.length;
function res(i, val) {
if (val && (typeof val === 'object' || typeof val === 'function')) {
// 如果val是Promise的實例
if (val instanceof Promise && val.then === Promise.prototype.then) {
// _state等于3 證明val實例的值也是一個Promise實例,把val替換成新的Promise實例
while (val._state === 3) {
val = val._value;
}
// resolved成功調(diào)用,遞歸處理resolved的值
if (val._state === 1) return res(i, val._value);
if (val._state === 2) reject(val._value);
// 處于padding狀態(tài)時調(diào)用then方法并手動處理值
val.then(function (val) {
res(i, val);
}, reject);
return;
} else {
// 如果不是promise的實例且包含then方法
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.all里面全部為fulFilled狀態(tài)后
if (--remaining === 0) {
resolve(args);
}
}
for (var i = 0; i < args.length; i++) {
res(i, args[i]);
}
});
};
第一行用到了iterableToArray函數(shù),這個函數(shù)的主要作用是把類數(shù)組轉(zhuǎn)換成可被遍歷的數(shù)組
const p1 = new Promise(() => {})
const p2 = new Promise(() => {})
console.log(Array.isArray(Promise.all[p1,p2])) //false
// 兼容Array.form這個es6語法
var iterableToArray = function (iterable) {
if (typeof Array.from === 'function') {
// ES2015+, iterables exist
iterableToArray = Array.from;
return Array.from(iterable);
}
// ES5, only arrays and array-likes exist
iterableToArray = function (x) { return Array.prototype.slice.call(x); };
return Array.prototype.slice.call(iterable);
}
使用Array.prototype.slice.call(x)對Array.from做了兼容
Promise.all = function (arr) {
var args = iterableToArray(arr);
return new Promise(function (resolve, reject) {
if (args.length === 0) return resolve([]);
var remaining = args.length;
function res(i, val) {
...
}
for (var i = 0; i < args.length; i++) {
res(i, args[i]);
}
});
};
先不要看res方法,先看看怎么處理傳進來的參數(shù)
循環(huán)調(diào)用res處理傳進來的每一項,第一個參數(shù)為下標(biāo),第二個參數(shù)是數(shù)組的每一項
再來看下res方法
function res(i, val) {
if (val && (typeof val === 'object' || typeof val === 'function')) {
// 如果val是Promise的實例
if (val instanceof Promise && val.then === Promise.prototype.then) {
// _state等于3 證明val實例的值也是一個Promise實例,把val替換成新的Promise實例
while (val._state === 3) {
val = val._value;
}
// resolved成功調(diào)用,遞歸處理resolved的值
if (val._state === 1) return res(i, val._value);
if (val._state === 2) reject(val._value);
// 處于padding狀態(tài)時調(diào)用then方法并手動處理值
val.then(function (val) {
res(i, val);
}, reject);
return;
} else {
// 如果不是promise的實例且包含then方法
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.all里面全部為fulFilled狀態(tài)后
if (--remaining === 0) {
resolve(args);
}
}
如果傳進來的val(args的每一項)不是對象或者function的話,那么直接視為結(jié)果值把args[i]給替換掉
args[i] = val;
如果傳進來的是一個Promise,則
if (val instanceof Promise && val.then === Promise.prototype.then) {
...
}
如果傳進來的val不是Promise且包含then方法,則
else {
// 如果不是promise的實例且包含then方法
var then = val.then;
if (typeof then === 'function') {
var p = new newPromise(then.bind(val));
p.then(function (val) {
res(i, val);
}, reject);
return;
}
}
重點在這一段
// _state等于3 證明val實例的值也是一個Promise實例,把val替換成新的Promise實例
while (val._state === 3) {
val = val._value;
}
// resolved成功調(diào)用,遞歸處理resolved的值
if (val._state === 1) return res(i, val._value);
if (val._state === 2) reject(val._value);
// 處于padding狀態(tài)時調(diào)用then方法并手動處理值
val.then(function (val) {
res(i, val);
}, reject);
return;
只有當(dāng)Promise的狀態(tài)為Fulfilled的時候,實例的value才會被正確的處理,否則會執(zhí)行return,所以只要有一個Promise未能成功Fulfilled都不會執(zhí)行resolve(args)
//滿足不了條件
if (--remaining === 0) {
resolve(args);
}
當(dāng)?shù)玫剿薪Y(jié)果值的時候(args[i] = val) == args[i] = val.value,調(diào)用resolve方法并傳入結(jié)果數(shù)組args
看個例子
const promise2 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('哈哈哈')
}, 700);
})
const promise3 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('哈哈哈2')
}, 600);
})
newPromise.all([promise2,promise3])
.then(res => {
console.log(res); //['哈哈哈','哈哈哈2']
})
這里無論什么時候resolve,最后得出的結(jié)果數(shù)組res都是按照Promise.all([])數(shù)組里面的順序來輸出的,這也印證了源碼中為什么要把下標(biāo)傳入到res()的原因(res(i, args[i]))
Promise.race
Promise.race = function (values) {
return new Promise(function (resolve, reject) {
iterableToArray(values).forEach(function(value){
Promise.resolve(value).then(resolve, reject);
});
});
};
Promise.race傳入一個數(shù)組,用Promise.resolve處理每一項并調(diào)用then方法
最后返回一個Promise實例
這里的Promise.resolve(value).then(resolve, reject)當(dāng)傳入的Promise誰的狀態(tài)首先變?yōu)?code style="margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">Fulfilled,誰就先調(diào)用then
因為Promise執(zhí)行resolve一次之后狀態(tài)就不會改變,所以race傳入多個Promise,誰的狀態(tài)先變?yōu)?code style="margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">Fulfilled,race就返回哪個
const promise2 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('哈哈哈')
}, 700);
})
const promise3 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('哈哈哈2')
}, 600);
})
Promise.race([promise2,promise3])
.then(res => {
console.log(res); //哈哈哈2
})
如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:
點個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點在看,都是耍流氓 -_-) 歡迎加我微信「TH0000666」一起交流學(xué)習(xí)... 關(guān)注公眾號「前端Sharing」,持續(xù)為你推送精選好文。
