帶你寫出符合Promise/A+規(guī)范Promise的源碼
Promise是前端面試中的高頻問題,如果你能根據(jù)PromiseA+的規(guī)范,寫出符合規(guī)范的源碼,那么我想,對(duì)于面試中的Promise相關(guān)的問題,都能夠給出比較完美的答案。
我的建議是,對(duì)照規(guī)范多寫幾次實(shí)現(xiàn),也許第一遍的時(shí)候,是改了多次,才能通過測試,那么需要反復(fù)的寫,我已經(jīng)將Promise的源碼實(shí)現(xiàn)寫了不下七遍,不那么聰明的話,當(dāng)然需要更加努力啦~

Promise的源碼實(shí)現(xiàn)
/**
?*?1.?new?Promise時(shí),需要傳遞一個(gè)?executor?執(zhí)行器,執(zhí)行器立刻執(zhí)行
?*?2.?executor?接受兩個(gè)參數(shù),分別是?resolve?和?reject
?*?3.?promise?只能從?pending?到?rejected,?或者從?pending?到?fulfilled
?*?4.?promise?的狀態(tài)一旦確認(rèn),就不會(huì)再改變
?*?5.?promise?都有?then?方法,then?接收兩個(gè)參數(shù),分別是?promise?成功的回調(diào)?onFulfilled,?
?*??????和?promise?失敗的回調(diào)?onRejected
?* 6. 如果調(diào)用 then 時(shí),promise已經(jīng)成功,則執(zhí)行 onFulfilled,并將promise的值作為參數(shù)傳遞進(jìn)去。
?*??????如果promise已經(jīng)失敗,那么執(zhí)行 onRejected, 并將 promise 失敗的原因作為參數(shù)傳遞進(jìn)去。
?*??????如果promise的狀態(tài)是pending,需要將onFulfilled和onRejected函數(shù)存放起來,等待狀態(tài)確定后,再依次將對(duì)應(yīng)的函數(shù)執(zhí)行(發(fā)布訂閱)
?*?7.?then?的參數(shù)?onFulfilled?和?onRejected?可以缺省
?*?8.?promise?可以then多次,promise?的then?方法返回一個(gè)?promise
?*?9.?如果?then?返回的是一個(gè)結(jié)果,那么就會(huì)把這個(gè)結(jié)果作為參數(shù),傳遞給下一個(gè)then的成功的回調(diào)(onFulfilled)
?*?10.?如果?then?中拋出了異常,那么就會(huì)把這個(gè)異常作為參數(shù),傳遞給下一個(gè)then的失敗的回調(diào)(onRejected)
?*?11.如果?then?返回的是一個(gè)promise,那么會(huì)等這個(gè)promise執(zhí)行完,promise如果成功,
?*???就走下一個(gè)then的成功,如果失敗,就走下一個(gè)then的失敗
?*/
const?PENDING?=?'pending';
const?FULFILLED?=?'fulfilled';
const?REJECTED?=?'rejected';
function?Promise(executor)?{
????let?self?=?this;
????self.status?=?PENDING;
????self.onFulfilled?=?[];//成功的回調(diào)
????self.onRejected?=?[];?//失敗的回調(diào)
????//PromiseA+?2.1
????function?resolve(value)?{
????????if?(self.status?===?PENDING)?{
????????????self.status?=?FULFILLED;
????????????self.value?=?value;
????????????self.onFulfilled.forEach(fn?=>?fn());//PromiseA+?2.2.6.1
????????}
????}
????function?reject(reason)?{
????????if?(self.status?===?PENDING)?{
????????????self.status?=?REJECTED;
????????????self.reason?=?reason;
????????????self.onRejected.forEach(fn?=>?fn());//PromiseA+?2.2.6.2
????????}
????}
????try?{
????????executor(resolve,?reject);
????}?catch?(e)?{
????????reject(e);
????}
}
Promise.prototype.then?=?function?(onFulfilled,?onRejected)?{
????//PromiseA+?2.2.1?/?PromiseA+?2.2.5?/?PromiseA+?2.2.7.3?/?PromiseA+?2.2.7.4
????onFulfilled?=?typeof?onFulfilled?===?'function'???onFulfilled?:?value?=>?value;
????onRejected?=?typeof?onRejected?===?'function'???onRejected?:?reason?=>?{?throw?reason?};
????let?self?=?this;
????//PromiseA+?2.2.7
????let?promise2?=?new?Promise((resolve,?reject)?=>?{
????????if?(self.status?===?FULFILLED)?{
????????????//PromiseA+?2.2.2
????????????//PromiseA+?2.2.4?---?setTimeout
????????????setTimeout(()?=>?{
????????????????try?{
????????????????????//PromiseA+?2.2.7.1
????????????????????let?x?=?onFulfilled(self.value);
????????????????????resolvePromise(promise2,?x,?resolve,?reject);
????????????????}?catch?(e)?{
????????????????????//PromiseA+?2.2.7.2
????????????????????reject(e);
????????????????}
????????????});
????????}?else?if?(self.status?===?REJECTED)?{
????????????//PromiseA+?2.2.3
????????????setTimeout(()?=>?{
????????????????try?{
????????????????????let?x?=?onRejected(self.reason);
????????????????????resolvePromise(promise2,?x,?resolve,?reject);
????????????????}?catch?(e)?{
????????????????????reject(e);
????????????????}
????????????});
????????}?else?if?(self.status?===?PENDING)?{
????????????self.onFulfilled.push(()?=>?{
????????????????setTimeout(()?=>?{
????????????????????try?{
????????????????????????let?x?=?onFulfilled(self.value);
????????????????????????resolvePromise(promise2,?x,?resolve,?reject);
????????????????????}?catch?(e)?{
????????????????????????reject(e);
????????????????????}
????????????????});
????????????});
????????????self.onRejected.push(()?=>?{
????????????????setTimeout(()?=>?{
????????????????????try?{
????????????????????????let?x?=?onRejected(self.reason);
????????????????????????resolvePromise(promise2,?x,?resolve,?reject);
????????????????????}?catch?(e)?{
????????????????????????reject(e);
????????????????????}
????????????????});
????????????});
????????}
????});
????return?promise2;
}
function?resolvePromise(promise2,?x,?resolve,?reject)?{
????let?self?=?this;
????//PromiseA+?2.3.1
????if?(promise2?===?x)?{
????????reject(new?TypeError('Chaining?cycle'));
????}
????if?(x?&&?typeof?x?===?'object'?||?typeof?x?===?'function')?{
????????let?used;?//PromiseA+2.3.3.3.3?只能調(diào)用一次
????????try?{
????????????let?then?=?x.then;
????????????if?(typeof?then?===?'function')?{
????????????????//PromiseA+2.3.3
????????????????then.call(x,?(y)?=>?{
????????????????????//PromiseA+2.3.3.1
????????????????????if?(used)?return;
????????????????????used?=?true;
????????????????????resolvePromise(promise2,?y,?resolve,?reject);
????????????????},?(r)?=>?{
????????????????????//PromiseA+2.3.3.2
????????????????????if?(used)?return;
????????????????????used?=?true;
????????????????????reject(r);
????????????????});
????????????}else{
????????????????//PromiseA+2.3.3.4
????????????????if?(used)?return;
????????????????used?=?true;
????????????????resolve(x);
????????????}
????????}?catch?(e)?{
????????????//PromiseA+?2.3.3.2
????????????if?(used)?return;
????????????used?=?true;
????????????reject(e);
????????}
????}?else?{
????????//PromiseA+?2.3.3.4
????????resolve(x);
????}
}
module.exports?=?Promise;
有專門的測試腳本可以測試所編寫的代碼是否符合PromiseA+的規(guī)范。
首先,在promise實(shí)現(xiàn)的代碼中,增加以下代碼:
Promise.defer?=?Promise.deferred?=?function?()?{
????let?dfd?=?{};
????dfd.promise?=?new?Promise((resolve,?reject)?=>?{
????????dfd.resolve?=?resolve;
????????dfd.reject?=?reject;
????});
????return?dfd;
}
安裝測試腳本:
npm?install?-g?promises-aplus-tests
如果當(dāng)前的promise源碼的文件名為promise.js
那么在對(duì)應(yīng)的目錄執(zhí)行以下命令:
promises-aplus-tests?promise.js
promises-aplus-tests中共有872條測試用例。以上代碼,可以完美通過所有用例。
對(duì)上面的代碼實(shí)現(xiàn)做一點(diǎn)簡要說明(其它一些內(nèi)容注釋中已經(jīng)寫得很清楚):
onFulfilled 和 onFulfilled的調(diào)用需要放在setTimeout,因?yàn)橐?guī)范中表示: onFulfilled or onRejected must not be called until the execution context stack contains only platform code。使用setTimeout只是模擬異步,原生Promise并非是這樣實(shí)現(xiàn)的。
在 resolvePromise 的函數(shù)中,為何需要usedd這個(gè)flag,同樣是因?yàn)橐?guī)范中明確表示: If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored. 因此我們需要這樣的flag來確保只會(huì)執(zhí)行一次。
self.onFulfilled 和 self.onRejected 中存儲(chǔ)了成功的回調(diào)和失敗的回調(diào),根據(jù)規(guī)范2.6顯示,當(dāng)promise從pending態(tài)改變的時(shí)候,需要按照順序去指定then對(duì)應(yīng)的回調(diào)。
PromiseA+的規(guī)范(翻譯版)
PS: 下面是我翻譯的規(guī)范,供參考
術(shù)語
promise 是一個(gè)有then方法的對(duì)象或者是函數(shù),行為遵循本規(guī)范
thenable 是一個(gè)有then方法的對(duì)象或者是函數(shù)
value 是promise狀態(tài)成功時(shí)的值,包括 undefined/thenable或者是 promise
exception 是一個(gè)使用throw拋出的異常值
reason 是promise狀態(tài)失敗時(shí)的值
要求
2.1 Promise States
Promise 必須處于以下三個(gè)狀態(tài)之一: pending, fulfilled 或者是 rejected
2.1.1 如果promise在pending狀態(tài)
2.1.1.1?可以變成?fulfilled?或者是?rejected
2.1.2 如果promise在fulfilled狀態(tài)
2.1.2.1?不會(huì)變成其它狀態(tài)
2.1.2.2?必須有一個(gè)value值
2.1.3 如果promise在rejected狀態(tài)
2.1.3.1?不會(huì)變成其它狀態(tài)
2.1.3.2?必須有一個(gè)promise被reject的reason
概括即是:promise的狀態(tài)只能從pending變成fulfilled,或者從pending變成rejected.promise成功,有成功的value.promise失敗的話,有失敗的原因
2.2 then方法
promise必須提供一個(gè)then方法,來訪問最終的結(jié)果
promise的then方法接收兩個(gè)參數(shù)
promise.then(onFulfilled,?onRejected)
2.2.1 onFulfilled 和 onRejected 都是可選參數(shù)
2.2.1.1?onFulfilled?必須是函數(shù)類型
2.2.1.2?onRejected?必須是函數(shù)類型
2.2.2 如果 onFulfilled 是函數(shù):
2.2.2.1?必須在promise變成?fulfilled?時(shí),調(diào)用?onFulfilled,參數(shù)是promise的value
2.2.2.2?在promise的狀態(tài)不是?fulfilled?之前,不能調(diào)用
2.2.2.3?onFulfilled?只能被調(diào)用一次
2.2.3 如果 onRejected 是函數(shù):
2.2.3.1?必須在promise變成?rejected?時(shí),調(diào)用?onRejected,參數(shù)是promise的reason
2.2.3.2?在promise的狀態(tài)不是?rejected?之前,不能調(diào)用
2.2.3.3?onRejected?只能被調(diào)用一次
2.2.4 onFulfilled 和 onRejected 應(yīng)該是微任務(wù)
2.2.5 onFulfilled ?和 onRejected 必須作為函數(shù)被調(diào)用
2.2.6 then方法可能被多次調(diào)用
2.2.6.1?如果promise變成了?fulfilled態(tài),所有的onFulfilled回調(diào)都需要按照then的順序執(zhí)行
2.2.6.2?如果promise變成了?rejected態(tài),所有的onRejected回調(diào)都需要按照then的順序執(zhí)行
2.2.7 then必須返回一個(gè)promise
promise2?=?promise1.then(onFulfilled,?onRejected);
2.2.7.1?onFulfilled?或?onRejected?執(zhí)行的結(jié)果為x,調(diào)用?resolvePromise
2.2.7.2?如果?onFulfilled?或者?onRejected?執(zhí)行時(shí)拋出異常e,promise2需要被reject
2.2.7.3?如果?onFulfilled?不是一個(gè)函數(shù),promise2?以promise1的值fulfilled
2.2.7.4?如果?onRejected?不是一個(gè)函數(shù),promise2?以promise1的reason?rejected
2.3 resolvePromise
resolvePromise(promise2, x, resolve, reject)
2.3.1 如果 promise2 和 x 相等,那么 reject promise with a TypeError
2.3.2 如果 x 是一個(gè) promsie
2.3.2.1?如果x是pending態(tài),那么promise必須要在pending,直到?x?變成?fulfilled?or?rejected.
2.3.2.2?如果?x?被?fulfilled,?fulfill?promise?with?the?same?value.
2.3.2.3?如果?x?被?rejected,?reject?promise?with?the?same?reason.
2.3.3 如果 x 是一個(gè) object 或者 是一個(gè) function
2.3.3.1?let?then?=?x.then.
2.3.3.2?如果?x.then?這步出錯(cuò),那么?reject?promise?with?e?as?the?reason..
2.3.3.3?如果?then?是一個(gè)函數(shù),then.call(x,?resolvePromiseFn,?rejectPromise)
????2.3.3.3.1?resolvePromiseFn 的?入?yún)⑹?y, 執(zhí)行 resolvePromise(promise2, y, resolve, reject);
????2.3.3.3.2?rejectPromise 的?入?yún)⑹?r, reject promise with?r.
????2.3.3.3.3?如果 resolvePromise 和 rejectPromise 都調(diào)用了,那么第一個(gè)調(diào)用優(yōu)先,后面的調(diào)用忽略。
????2.3.3.3.4?如果調(diào)用then拋出異常e?
????????2.3.3.3.4.1?如果?resolvePromise?或?rejectPromise?已經(jīng)被調(diào)用,那么忽略
????????2.3.3.3.4.3?否則,reject?promise?with?e?as?the?reason
2.3.3.4?如果?then?不是一個(gè)function.?fulfill?promise?with?x.
2.3.4 如果 x 不是一個(gè) object 或者 function,fulfill promise with x.
Promise的其他方法
雖然上述的promise源碼已經(jīng)符合PromiseA+的規(guī)范,但是原生的Promise還提供了一些其他方法,如:
Promise.resolve()
Promise.reject()
Promise.prototype.catch()
Promise.prototype.finally()
Promise.all()
Promise.race()
下面具體說一下每個(gè)方法的實(shí)現(xiàn):
Promise.resolve
Promise.resolve(value) 返回一個(gè)以給定值解析后的Promise 對(duì)象.
如果 value 是個(gè) thenable 對(duì)象,返回的promise會(huì)“跟隨”這個(gè)thenable的對(duì)象,采用它的最終狀態(tài)
如果傳入的value本身就是promise對(duì)象,那么Promise.resolve將不做任何修改、原封不動(dòng)地返回這個(gè)promise對(duì)象。
其他情況,直接返回以該值為成功狀態(tài)的promise對(duì)象。
Promise.resolve?=?function?(param)?{
????????if?(param?instanceof?Promise)?{
????????return?param;
????}
????return?new?Promise((resolve,?reject)?=>?{
????????if?(param?&&?typeof?param?===?'object'?&&?typeof?param.then?===?'function')?{
????????????setTimeout(()?=>?{
????????????????param.then(resolve,?reject);
????????????});
????????}?else?{
????????????resolve(param);
????????}
????});
}
thenable對(duì)象的執(zhí)行加 setTimeout的原因是根據(jù)原生Promise對(duì)象執(zhí)行的結(jié)果推斷的,如下的測試代碼,原生的執(zhí)行結(jié)果為: 20 ?400 ?30;為了同樣的執(zhí)行順序,增加了setTimeout延時(shí)。
測試代碼:
let?p?=?Promise.resolve(20);
p.then((data)?=>?{
????console.log(data);
});
let?p2?=?Promise.resolve({
????then:?function(resolve,?reject)?{
????????resolve(30);
????}
});
p2.then((data)=>?{
????console.log(data)
});
let?p3?=?Promise.resolve(new?Promise((resolve,?reject)?=>?{
????resolve(400)
}));
p3.then((data)?=>?{
????console.log(data)
});
Promise.reject
Promise.reject方法和Promise.resolve不同,Promise.reject()方法的參數(shù),會(huì)原封不動(dòng)地作為reject的理由,變成后續(xù)方法的參數(shù)。
Promise.reject?=?function?(reason)?{
????return?new?Promise((resolve,?reject)?=>?{
????????reject(reason);
????});
}Promise.prototype.catch
Promise.prototype.catch 用于指定出錯(cuò)時(shí)的回調(diào),是特殊的then方法,catch之后,可以繼續(xù) .then
Promise.prototype.catch?=?function?(onRejected)?{
????return?this.then(null,?onRejected);
}
Promise.prototype.finally
不管成功還是失敗,都會(huì)走到finally中,并且finally之后,還可以繼續(xù)then。并且會(huì)將值原封不動(dòng)的傳遞給后面的then.
Promise.prototype.finally?=?function?(callback)?{
????return?this.then((value)?=>?{
????????return?Promise.resolve(callback()).then(()?=>?{
????????????return?value;
????????});
????},?(err)?=>?{
????????return?Promise.resolve(callback()).then(()?=>?{
????????????throw?err;
????????});
????});
}
Promise.all
Promise.all(promises) 返回一個(gè)promise對(duì)象
如果傳入的參數(shù)是一個(gè)空的可迭代對(duì)象,那么此promise對(duì)象回調(diào)完成(resolve),只有此情況,是同步執(zhí)行的,其它都是異步返回的。
如果傳入的參數(shù)不包含任何 promise,則返回一個(gè)異步完成.
promises 中所有的promise都promise都“完成”時(shí)或參數(shù)中不包含 promise 時(shí)回調(diào)完成。
如果參數(shù)中有一個(gè)promise失敗,那么Promise.all返回的promise對(duì)象失敗
在任何情況下,Promise.all 返回的 promise 的完成狀態(tài)的結(jié)果都是一個(gè)數(shù)組
Promise.all?=?function?(promises)?{
????promises?=?Array.from(promises);//將可迭代對(duì)象轉(zhuǎn)換為數(shù)組
????return?new?Promise((resolve,?reject)?=>?{
????????let?index?=?0;
????????let?result?=?[];
????????if?(promises.length?===?0)?{
????????????resolve(result);
????????}?else?{
????????????function?processValue(i,?data)?{
????????????????result[i]?=?data;
????????????????if?(++index?===?promises.length)?{
????????????????????resolve(result);
????????????????}
????????????}
????????????for?(let?i?=?0;?i???????????????????//promises[i]?可能是普通值
??????????????????Promise.resolve(promises[i]).then((data)?=>?{
????????????????????processValue(i,?data);
????????????????},?(err)?=>?{
????????????????????reject(err);
????????????????????return;
????????????????});
????????????}
????????}
????});
}
測試代碼:
var?promise1?=?new?Promise((resolve,?reject)?=>?{
????resolve(3);
})
var?promise2?=?42;
var?promise3?=?new?Promise(function(resolve,?reject)?{
??setTimeout(resolve,?100,?'foo');
});
Promise.all([promise1,?promise2,?promise3]).then(function(values)?{
??console.log(values);?//[3,?42,?'foo']
},(err)=>{
????console.log(err)
});
var?p?=?Promise.all([]);?//?will?be?immediately?resolved
var?p2?=?Promise.all([1337,?"hi"]);?//?non-promise?values?will?be?ignored,?but?the?evaluation?will?be?done?asynchronously
console.log(p);
console.log(p2)
setTimeout(function(){
????console.log('the?stack?is?now?empty');
????console.log(p2);
});
Promise.race
Promise.race函數(shù)返回一個(gè) Promise,它將與第一個(gè)傳遞的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失?。╮ejects),這要取決于第一個(gè)完成的方式是兩個(gè)中的哪個(gè)。
如果傳的參數(shù)數(shù)組是空,則返回的 promise 將永遠(yuǎn)等待。
如果迭代包含一個(gè)或多個(gè)非承諾值和/或已解決/拒絕的承諾,則 Promise.race 將解析為迭代中找到的第一個(gè)值。
Promise.race?=?function?(promises)?{
????promises?=?Array.from(promises);//將可迭代對(duì)象轉(zhuǎn)換為數(shù)組
????return?new?Promise((resolve,?reject)?=>?{
????????if?(promises.length?===?0)?{
????????????return;
????????}?else?{
????????????for?(let?i?=?0;?i?????????????????Promise.resolve(promises[i]).then((data)?=>?{
????????????????????resolve(data);
????????????????????return;
????????????????},?(err)?=>?{
????????????????????reject(err);
????????????????????return;
????????????????});
????????????}
????????}
????});
}
測試代碼:
Promise.race([
????new?Promise((resolve,?reject)?=>?{?setTimeout(()?=>?{?resolve(100)?},?1000)?}),
????undefined,
????new?Promise((resolve,?reject)?=>?{?setTimeout(()?=>?{?reject(100)?},?100)?})
]).then((data)?=>?{
????console.log('success?',?data);
},?(err)?=>?{
????console.log('err?',err);
});
Promise.race([
????new?Promise((resolve,?reject)?=>?{?setTimeout(()?=>?{?resolve(100)?},?1000)?}),
????new?Promise((resolve,?reject)?=>?{?setTimeout(()?=>?{?resolve(200)?},?200)?}),
????new?Promise((resolve,?reject)?=>?{?setTimeout(()?=>?{?reject(100)?},?100)?})
]).then((data)?=>?{
????console.log(data);
},?(err)?=>?{
????console.log(err);
});
參考
Promise A+ 規(guī)范
ES6 Promise
Promise MDN
學(xué)習(xí)交流
- 關(guān)注公眾號(hào)【前端宇宙】,每日獲取好文推薦
- 添加微信,入群交流
“在看和轉(zhuǎn)發(fā)”就是最大的支持
