目錄 (づ ̄ 3 ̄)づ=> So沒(méi)有一個(gè)目錄真的很惱火。。
[TOC]
序
本文會(huì)對(duì)Promise規(guī)范進(jìn)行一個(gè)比較完整的實(shí)現(xiàn),目的是為了加深對(duì)Promise各個(gè)特性的理解從而更好的應(yīng)用。
[warning] 注意:本文依據(jù)Promises/A+規(guī)范進(jìn)行Promise的實(shí)現(xiàn)
1.Promise/A+ 術(shù)語(yǔ)
1.1. promise
promise是一個(gè)對(duì)象或則函數(shù),它的表現(xiàn)是依據(jù)Promises/A+這篇規(guī)范說(shuō)明來(lái)定義的。
1.1. promise is an object or function with a then method whose behavior conforms to this specification.
1.2. theable
thenable是一個(gè)定義了then方法的對(duì)象或則函數(shù)。
thenable is an object or function that defines a then method.
1.3. value
value可以是任何合法的JS值,甚至包括undefined、一個(gè)thenable、一個(gè)promise。
value is any legal JavaScript value (including undefined, a thenable, or a promise).
1.4. exception
exception是一個(gè)用throw語(yǔ)句拋出的值。
exception is a value that is thrown using the throw statement.
1.5. reason
reason是一個(gè)為什么promise會(huì)被拒絕的理由。
reason is a value that indicates why a promise was rejected.
Promise規(guī)范要求
判斷一個(gè)東東是不是Promise,有三項(xiàng)主要的特征可作為參考
- Promise有三種狀態(tài)
pending、fulfilled、rejected - Promise含有then方法
- Promise含有
Promise Resolution Procedure(promise的狀態(tài)轉(zhuǎn)換處理方法)。
2.1. Promise狀態(tài)
一個(gè)promise必須處于 pending 、fulfilled、rejected 三種狀態(tài)中的其中一種
下面是一個(gè)promise最基本的使用demo,我們先有個(gè)印象。
- 其中promise實(shí)例化的時(shí)候傳入了一個(gè)函數(shù)作為參數(shù),這個(gè)函數(shù)我們稱(chēng)之為
executor,它能告訴我們何時(shí)將promise狀態(tài)從pending轉(zhuǎn)化為其余兩態(tài)中的一態(tài)。 - 而
then方法是實(shí)例化對(duì)象下的一個(gè)方法,它能傳入兩個(gè)參數(shù),一般是兩個(gè)回調(diào)函數(shù),對(duì)應(yīng)fulfilled和rejected兩個(gè)狀態(tài),當(dāng)promise從pengding狀態(tài)轉(zhuǎn)化成其中一個(gè)狀態(tài)時(shí)就會(huì)觸發(fā)對(duì)應(yīng)的回調(diào)函數(shù)。
let p = new Promise((resolve,reject)=>{
let x = Math.random();
console.log(x);
if (x > .5) {
resolve('我是你許下的諾言的那個(gè)東東');
} else {
reject('我是你未能實(shí)現(xiàn)諾言的理由');
}
});
p.then((value)=>{ //綁定成功時(shí)的回調(diào)函數(shù)
console.log('fulfilled:',value); //fulfilled:我是你許下的諾言的那個(gè)東東
},(reason)=>{ //綁定失敗時(shí)的回調(diào)函數(shù)
console.log('rejected:',reason); //rejected:我是你未能實(shí)現(xiàn)諾言的理由
});
2.1.1. pending狀態(tài)
當(dāng)Promise處于pending狀態(tài)時(shí),它可能轉(zhuǎn)換為fulfilled或則rejected狀態(tài)。
When pending, a promise:may transition to either the fulfilled or rejected state.
2.1.2. fulfilled狀態(tài)
當(dāng)Promise處于fulfilled狀態(tài)時(shí),它不再能轉(zhuǎn)換為其它狀態(tài) 且 它必須有一個(gè)值,這個(gè)值不能被更改。
When fulfilled, a promise:
- must not transition to any other state.
- must have a value, which must not change.
2.1.3 rejected狀態(tài)
當(dāng)promise處于rejected時(shí),它不再能轉(zhuǎn)換為其它狀態(tài) 且 它必須有一個(gè)理由,這個(gè)理由不能被更改。
When rejected, a promise:
- must not transition to any other state.
- must have a reason, which must not change.
[danger]注意: 當(dāng)promise處于fulfilled或則rejected時(shí),它都有一個(gè)值,這個(gè)值不能被更改,但是可以像使用常量一樣在這個(gè)值下面掛載其它值。
Here, “must not change” means immutable identity (i.e. ===), but does not imply deep immutability.
2.1. Promise實(shí)現(xiàn)
請(qǐng)先回顧一下我們?cè)谡f(shuō)Promise狀態(tài)時(shí)候最初的那個(gè)demo
我們通過(guò)實(shí)例化Promise時(shí)傳入了一個(gè)參數(shù),這個(gè)參數(shù)是一個(gè)執(zhí)行函數(shù)(executor),它能決定什么時(shí)候?qū)romise轉(zhuǎn)換成fulfilled什么時(shí)候轉(zhuǎn)換成rejected。
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function Promise(executor){
let self = this; //緩存下
self.value = undefined; //用來(lái)存放value和reason,因?yàn)閜romise只會(huì)處于一種狀態(tài)故可只用一個(gè)變量來(lái)表示。
self.status = PENDING; //將初始狀態(tài)設(shè)置為pending
self.onFulfilledCallbacks = []; //用來(lái)存放所有成功的回調(diào)函數(shù)
self.onRejectedCallbacks = []; //用來(lái)存放所有失敗的回調(diào)函數(shù)
try{
executor(resolve,reject); //調(diào)用執(zhí)行函數(shù),將resolve和reject方法作為參數(shù)傳入
}catch (e){
reject(e); //若執(zhí)行函數(shù)中存在異常直接用拋出的值來(lái)拒絕promise
}
//-----------------------------------------------------------------------------------------------------------
function resolve(value){ //此方法會(huì)隨著executor傳入而傳入
setTimeout(function(){
if(self.status === PENDING){ //確保狀態(tài)只會(huì)改變一次
self.status = FULFILLED; //改變狀態(tài)
self.value = value; //賦予一個(gè)值
self.onFulfilledCallbacks.forEach(cb => cb(self.value)); //2.2.2. //2.2.6.
}
})
}
function reject(reason){
setTimeout(function(){
if(self.status === PENDING){
self.status = REJECTED;
self.value = reason;
self.onRejectedCallbacks.forEach(cb => cb(self.value));
}
})
}
}
以上實(shí)現(xiàn)了2.1. ,promise的三種狀態(tài)以及狀態(tài)之間的改變。
executor,形參、實(shí)參、作用域鏈
我們可以發(fā)現(xiàn)最終轉(zhuǎn)換狀態(tài)時(shí)通過(guò)Promise內(nèi)部的兩個(gè)方法resolve和reject,這個(gè)兩個(gè)方法是在什么時(shí)候傳入的呢?
一個(gè)函數(shù)的參數(shù)查找,是從調(diào)用這個(gè)函數(shù)時(shí)所處的作用域開(kāi)始查找的。
new Promise傳入的executor,是參數(shù)也是對(duì)executor函數(shù)的定義,此時(shí)executor的resolve和reject為形參。
我們new Promise的時(shí)候,會(huì)執(zhí)行構(gòu)造函數(shù)Promise內(nèi)的代碼,也就是在這時(shí)executor被執(zhí)行,而executor此時(shí)所處的作用域是在Promise構(gòu)造函數(shù)內(nèi)部,resolve和reject方法作為實(shí)參被傳入。
2.2. then方法
一個(gè)promise必須提供一個(gè)then方法來(lái)使用它將要或則說(shuō)已經(jīng)被賦予的 value 或則 reason,一個(gè)promise的then方法接收兩個(gè)參數(shù)
promise.then(onFulfilled,onRejected)
2.2.1. then參數(shù)
then中的參數(shù)皆為可選參數(shù),如果onFulfilled或則說(shuō)onRejected不是一個(gè)函數(shù),那么將會(huì)被忽略。
Both onFulfilled and onRejected are optional arguments:
- If onFulfilled is not a function, it must be ignored.
- If onRejected is not a function, it must be ignored.
2.2.2. 如果onFulfilled是一個(gè)函數(shù)
- 如果onFulfilled是一個(gè)函數(shù),它必須在promise狀態(tài)轉(zhuǎn)換為fulfilled時(shí)候就被調(diào)用,并且promise被賦予的value會(huì)成為這個(gè)函數(shù)(onFulfilled)的第一個(gè)參數(shù)。
- onFulfilled不能在promise狀態(tài)轉(zhuǎn)化為fulfilled前就調(diào)用
- onFulfilled函數(shù)不能重復(fù)調(diào)用
原文規(guī)范詳見(jiàn)Promises/A+
2.2.3. 如果onRejected是一個(gè)函數(shù)
- 如果onRejected是一個(gè)函數(shù),它必須在promise狀態(tài)轉(zhuǎn)換為rejected時(shí)候就被調(diào)用,并且promise被賦予的reason會(huì)成為這個(gè)函數(shù)(onRejected)的第一個(gè)參數(shù)。
- onRejected不能在promise狀態(tài)轉(zhuǎn)化為rejected前就調(diào)用
- onRejected函數(shù)不能重復(fù)調(diào)用
2.2.4. onFulfilled 或則 onRejected 必須在執(zhí)行棧 只存在 platform code 時(shí)才能被調(diào)用。
2.2.5. onFulfilled 和 onRejected 必須被當(dāng)做函數(shù)調(diào)用。
2.2.6. 同一個(gè)promise實(shí)例可以調(diào)用多次then
- 當(dāng)一個(gè)promise轉(zhuǎn)化為fulfilled狀態(tài),所有onFulfilled callback會(huì)按照回調(diào)函數(shù)通過(guò)then添加時(shí)的順序而執(zhí)行。
- 當(dāng)一個(gè)promise轉(zhuǎn)化為rejected狀態(tài),所有onRejected callback會(huì)按照回調(diào)函數(shù)通過(guò)then添加時(shí)的順序而執(zhí)行。
釋:then在同一個(gè)promise實(shí)例下多次調(diào)用,意味著可以在同一個(gè)promise的同一種狀態(tài)下綁定多個(gè)不同的回調(diào)函數(shù),而這些回調(diào)函數(shù)執(zhí)行的順序和它們被綁定時(shí)的順序相同。
2.2.7. then必會(huì)返回一個(gè)新的promise
promise2 = promise1.then(onFulfilled,onRejected);
- 如果onFulfilled或onRejected回調(diào)函數(shù)中返回了一個(gè)值,假定為x,那么調(diào)用一個(gè) promise解析方法
[[Resolve]](promise2,x)。 - 如果onFulfilled或者onRejected拋出了一個(gè)
exception(異常)e,promise2必須以這個(gè)e作為reason來(lái)拒絕promise,使其狀態(tài)改變?yōu)閞ejected。 - 如果onFulfilled不是一個(gè)函數(shù)且
promise1的狀態(tài)為fulfilled,promise2必須以promise1的值來(lái)fulfilled。 - 如果onRejected不是一個(gè)函數(shù)且
promise1的狀態(tài)為rejected,promise2必須以promise1的理由來(lái)rejected。
2.2. Promise實(shí)現(xiàn)
2.2.提的是一個(gè)then的實(shí)現(xiàn)規(guī)則,而then主要作用為promise綁定回調(diào)函數(shù),當(dāng)promise轉(zhuǎn)換狀態(tài)時(shí)會(huì)自動(dòng)調(diào)用對(duì)應(yīng)的回調(diào)函數(shù)。(對(duì)應(yīng)規(guī)范2.2.2-2.2.3)
其實(shí)就是發(fā)布訂閱模式啦
function Promise(){
...
function resolve(value){
setTimeout(function(){ //2.2.4.
if(self.status === PENDING){ //2.2.2.3-2.2.2.4
self.status = FULFILLED;
self.value = value;
self.onFulfilledCallbacks.forEach(cb => cb(self.value)); //2.2.6.
}
})
}
function reject(reason){
setTimeout(function(){
if(self.status === PENDING){ //2.2.3.3-2.2.3.4
self.status = REJECTED;
self.value = reason;
self.onRejectedCallbacks.forEach(cb => cb(self.value));
}
})
}
}
//---------------------------------------------------------------------------------------------------
Promise.prototype.then = function (onFulfilled, onRejected) { //2.2.1.
//2.2.7.3-2.2.7.4 //2.2.5.
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};
let self = this,
promise2; //2.2.7.0 //聲明要返回的promise2
if(self.status === PENDING){
//2.2.7.
return promise2 = new Promise(function(resolve,reject){
//存儲(chǔ)then方法綁定的回調(diào)函數(shù) //2.2.6.
self.onFulfilledCallbacks.push((value)=>{
try{
let x = onFulfilled(value);
resolvePromise(promise2,x,resolve,reject); //2.2.7.1
}catch (e){
reject(e); //2.2.7.2
}
});
self.onRejectedCallbacks.push((reason)=>{
try{
let x= onRejected(reason);
resolvePromise(promise2,x,resolve,reject);
}catch (e){
reject(e);
}
});
});
}
};
關(guān)于platform code
Here “platform code” means engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a “macro-task” mechanism such as setTimeout or setImmediate, or with a “micro-task” mechanism such as MutationObserver or process.nextTick. Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or “trampoline” in which the handlers are called.
上面一大段話(huà)的意思大致上就是要求 onFulfilled 和 onRejected 回調(diào)函數(shù)確保異步執(zhí)行。我們可以選擇用宏任務(wù)(setTimeout/setImmediate)或則微任務(wù)(process.nextTix/MutationObserver)來(lái)完成這項(xiàng)規(guī)范。
這里我們通過(guò)在Promise中的resolve和reject方法中套了一個(gè)setTimeout()來(lái)實(shí)現(xiàn)。
function resolve(value){
setTimeout(function(){ //2.2.4.
if(self.status === PENDING){ //2.2.2.3-2.2.2.4
self.status = FULFILLED;
self.value = value;
self.onFulfilledCallbacks.forEach(cb => cb(self.value)); //2.2.6.
}
})
}
這樣setTimeout中的代碼就會(huì)在下一個(gè)新的執(zhí)行棧中執(zhí)行。即使executor中的代碼是同步代碼也一樣。
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('resolve');
})
});
p.then((value)=>{
console.log('fulfilled:',value);
},(reason)=>{
console.log('rejected:',reason);
});
console.log('----------------');
//輸出
>>>----------------
>>>fulfilled: resolve
//----------------------------------------------------------------------------------
let p = new Promise((resolve,reject)=>{
resolve('resolve');
});
p.then((value)=>{
console.log('fulfilled:',value);
},(reason)=>{
console.log('rejected:',reason);
});
console.log('----------------');
//輸出
>>>----------------
>>>fulfilled: resolve
情景:值的穿透
下面的例子中本應(yīng)是第一個(gè)then中的參數(shù)會(huì)穿透到第二then中作為參數(shù)。
下面兩句再集合resolvePromise方法即是穿透原因
Promise.prototype.then = function (onFulfilled, onRejected) { //2.2.1.
//2.2.7.3-2.2.7.4 //2.2.5.
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; //結(jié)合resolvePromise方法即是穿透原因
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}; //繼續(xù)把異常往后拋
...
//-------------------------------------------------
let p = new Promise((resolve,reject)=>{
resolve('resolve');
});
p.then().then((value)=>{
console.log(value); //會(huì)輸出resolve
});
2.3. Promise狀態(tài)解析方法(promise resolution procedure)
let x= onRejected(reason);
resolvePromise(promise2,x,resolve,reject); //resolve/reject為promise2的resolve/reject
Promise狀態(tài)解析方法的作用是將then時(shí)返回的promise2的狀態(tài)改變并賦予其vlaue/reason。
- 如果
x是一個(gè)thenable,那么該方法將試圖將以x的狀態(tài)來(lái)改變promise2的狀態(tài) - 否則就將
promise2改成fulfilled狀態(tài),并且value即為x的值
2.3.1. 如果 promise2 和 x 是引用關(guān)系,則拋出一個(gè) TypeError 做為理由來(lái) reject promise2。
2.3.2. 如果 x 是一個(gè)promise ,讓promise2采用它的狀態(tài)。
- 如果
x處于pending,promise2 必須保持pending直到 x 轉(zhuǎn)換為 fulfilled或則rejected。 - 如果
x是fulfilled狀態(tài),讓promise2也為fulfilled,并且讓promise2的value為x的value。 - 如果
x是rejected狀態(tài),讓promise2也為rejected,并且讓promise2的value為x的reason。
2.3.3. 如果 x 是一個(gè)對(duì)象或則函數(shù)
- Let
thenbex.then - 如果檢索
x.then時(shí)候拋出了一個(gè)異常e,那么以這個(gè)e來(lái)rejectepromise2。 - 如果
then是一個(gè)函數(shù),用x作為this,resolvePromise作為第一個(gè)參數(shù),rejectPromise作為第二個(gè)參數(shù)來(lái)call它。- 如果
resolvePromise被調(diào)用,循環(huán)調(diào)用promise狀態(tài)解析方法(原本的x替換為調(diào)用resolvePromise傳入的參數(shù),假定為y)。 - 如果
rejectPromise被調(diào)用,則reject Promise2,reason為調(diào)用rejectPromise傳入的參數(shù) - 如果
resolvePromise和rejectPromise同時(shí)被調(diào)用或則多次調(diào)用,那么第一個(gè)調(diào)用的擁有優(yōu)先權(quán),其它的會(huì)被忽略。 - 如果調(diào)用
then的時(shí)候拋出了一個(gè)異常e- 如果
resolvePromise或rejectPromise已經(jīng)被調(diào)用,則忽略它。 - 否則,則用這個(gè)
e來(lái)rejectpromise2。
- 如果
- 如果
- 如果then不是一個(gè)函數(shù),則用
x來(lái)fulfilledpromise2
2.3.4. 如果 x 不是一個(gè)函數(shù)也不是一個(gè)對(duì)象,則用x來(lái)fulfilledpromise2
2.3.3. Promise實(shí)現(xiàn)
resolvePromise方法針對(duì)的是then綁定的回調(diào)函數(shù)中的return值進(jìn)行解析,一般情況是:
- 當(dāng)return的是普通類(lèi)型的值,那么會(huì)以這個(gè)值來(lái)fulfilled promise2
- 如果是一個(gè)promise,那么會(huì)以這個(gè)x promise的結(jié)果來(lái)fulfilled/rejected promise2
function resolve(value) {
if(value instanceof Promise){ //和resolvePromise有點(diǎn)聯(lián)系的是 當(dāng)then return的promise中又resolve了一個(gè)promise會(huì)先走這,會(huì)將resolve里的promise的值賦給調(diào)用resolve的promise(說(shuō)法欠妥,意會(huì)即可)
return value.then(resolve,reject); //這意味著如果promise1 resolve中是一個(gè)promise2,那么promise1狀態(tài)的改變時(shí)間會(huì)被推遲,直到promise2狀態(tài)改變調(diào)用promise2的回調(diào)時(shí),promise1狀態(tài)才會(huì)改變才會(huì)觸發(fā)promise1的回調(diào)
}
...
//---------------------------------------------------------------------------------------------------------
function resolvePromise(promise2,x,resolve,reject){
if(x === promise2){ //2.3.1.
return reject(new TypeError('禁止循環(huán)引用!'));
}
let called =false;
//2.3.2.
if(x instanceof Promise){
if(x.status === PENDING){ //2.3.2.1
x.then((y)=>{
resolvePromise(promise2,y,resolve,reject); //因?yàn)榇藭r(shí)的y,有可能也是一個(gè)promise //掛上一個(gè)鉤子只要x狀態(tài)轉(zhuǎn)化為成功態(tài)就遞歸調(diào)用resolvePromise
},reject);
}else{ //此分支存在的意義在于若executor調(diào)用resolve/reject不是異步的且不在resolve/reject中設(shè)置setTimeout,意味著當(dāng)new的時(shí)候就會(huì)返回一個(gè)帶狀態(tài)的promise就會(huì)走這里。
x.then(resolve,reject); //2.3.2.2-2.3.2.3 //只要x狀態(tài)改變,就以x的狀態(tài)和值來(lái)改變promise2的狀態(tài)和值 //這個(gè)值可能是一個(gè)promise,前提是在上面那種假設(shè)實(shí)現(xiàn)中 //如果不符合上面那種實(shí)現(xiàn)且不想像規(guī)范一樣允許值可以為一個(gè)promise或則對(duì)象 可除去此分支
}
}else if(x!=null&&((typeof x === 'function')||(typeof x === 'object'))){ //2.3.3.
try{
let then = x.then; //2.3.3.1
if(typeof then === 'function'){
//2.3.3.3.
then.call(x,(y)=>{
if(called) return; //2.3.3.3.3.
called = true;
resolvePromise(promise2,y,resolve,reject); //在resolve中又包含promise的情況下,由于resolve中的 value.then存在,當(dāng)前回調(diào)調(diào)用時(shí),resolve中的promise狀態(tài)一定已經(jīng)改變,在狀態(tài)已經(jīng)改變的時(shí)候利用then綁定回調(diào),會(huì)走then中的status==fulfilled或則rejected分支
},(reason)=>{
if(called) return;
called = true;
reject(reason);
});
}else{
resolve(x); //2.3.3.4. //1.3
}
}catch (e){
if(called) return; //2.3.3.3.4.1.
called = true;
reject(e); //2.3.3.2. //2.3.3.3.4.2.
}
}else{ //2.3.4.
resolve(x);
}
}
情景:當(dāng)return的是promise且該promise的resolve/reject ()中 也是一個(gè)promise
let p = new Promise((resolve,reject)=>{
resolve('resolve1');
});
p.then((value)=>{
return new Promise((resolve,reject)=>{
resolve(new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('別慫')
});
}));
});
}).then((value)=>{
console.log(value); //別慫
});
console.log('----------------');
可見(jiàn)最終的value值為最里層的value值,這樣的實(shí)現(xiàn)關(guān)鍵在于遞歸調(diào)用resolvePromise。
...
function resolve(value) {
if(value instanceof Promise){
return value.then(resolve,reject);
...
if(x instanceof Promise){
if(x.status === PENDING){ //2.3.2.1
x.then((y)=>{
resolvePromise(promise2,y,resolve,reject);
},reject);
}else{
x.then(resolve,reject);
}
}
以上這段代碼,當(dāng)promise1執(zhí)行回調(diào)的時(shí)候,會(huì)將x傳入resolvePromise執(zhí)行,此時(shí)由于resolve()方法中的setTimeout,該x是pending狀態(tài)進(jìn)pending分支,該分支會(huì)為X掛上一個(gè)鉤子,當(dāng)它狀態(tài)轉(zhuǎn)換后會(huì)再次調(diào)用resolvePromise。
- 如果x的resolve中傳入的也是一個(gè)promise (y),由于resolve中添加的value.then,它會(huì)推遲x的狀態(tài)轉(zhuǎn)換,這意味著X狀態(tài)轉(zhuǎn)換時(shí),y的狀態(tài)一定已經(jīng)轉(zhuǎn)換,于是會(huì)走下面那個(gè)分支,調(diào)用y.then,而因?yàn)閥的狀態(tài)已經(jīng)轉(zhuǎn)換,在then方法中此時(shí)就不再能通過(guò)狀態(tài)改變時(shí)觸發(fā)回調(diào)函數(shù),故要支持此功能需要在then中添加
self.status===FULFILLED/REJECTED分支。
}else if(self.status === FULFILLED){
return promise2 = new Promise(function(resolve,reject){
setTimeout(function(){
try{
let x =onFulfilled(self.value);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
})
});
}else{
return promise2 = new Promise(function(resolve,reject){
setTimeout(function(){
try{
let x =onRejected(self.value);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
})
});
}
這里用了setTimeout是為了確?;卣{(diào)函數(shù)會(huì)異步執(zhí)行。(針對(duì)2.2.4.)
- 如果x的resolve傳入的只是一個(gè)普通的值。。。呵呵噠,那就直接resolve(x)咯
[warning] 值得注意的是: 如果沒(méi)有在
resolve()方法中對(duì)value進(jìn)行判斷,那么此時(shí)嵌套promise中再嵌套一層promise輸出結(jié)果會(huì)是一個(gè)promise。因?yàn)榈诙€(gè)promise不會(huì)等第三個(gè)promise狀態(tài)轉(zhuǎn)換后才轉(zhuǎn)換狀態(tài),這意味著第二個(gè)promise的值就為第三個(gè)promise對(duì)象。
情景:當(dāng)new的promise中的resolve也是一個(gè)promise,而這個(gè)promise的resolve中又是一個(gè)promise...
此時(shí)情況同上個(gè)情景,得益于then()中對(duì)value的判斷,它會(huì)推遲父promise狀態(tài)的轉(zhuǎn)變。
如果沒(méi)有這個(gè)判斷和推遲,那么也可能最終得到的value是個(gè)promise對(duì)象。(這是規(guī)范允許的,但NodeJS和blubird對(duì)promise規(guī)范的實(shí)現(xiàn)都對(duì)父promise的狀態(tài)轉(zhuǎn)換進(jìn)行了推遲)
情景:在一個(gè)已經(jīng)轉(zhuǎn)換了狀態(tài)的promise中再次調(diào)用這個(gè)promise的then方法
此時(shí)也會(huì)走then中的self.status === FULFILLED/REJECTED 的分支,再次證明需要在then中添加這兩個(gè)分支并用上settimeout
p1.then((value)=>{ //執(zhí)行此回調(diào)時(shí)p1狀態(tài)已經(jīng)改變
p1.then(...);
});
x instanceof Promise 和 typeof x=function... 遞歸的區(qū)別
instance分支下的遞歸 因?yàn)榇嬖趯?duì)promise狀態(tài)的判斷,當(dāng)resolve()沒(méi)有對(duì)value進(jìn)行判斷時(shí),instance分支下的結(jié)果value最終可能為promise對(duì)象,而x.then分支下因?yàn)闆](méi)有對(duì)promise狀態(tài)進(jìn)行判斷,故不會(huì)出現(xiàn)value為promise對(duì)象的情況。
其余Promise方法的實(shí)現(xiàn)
Promise.prototype.catch
此方法實(shí)現(xiàn)灰常簡(jiǎn)單,只需在最后一個(gè)then綁定完回調(diào)后再綁定一個(gè)錯(cuò)誤的回調(diào)即可
promise.prototype.catch = function(onRejected){
this.then(null,onRejected);
}
Promise.all
此方法傳入一組promise實(shí)例再返回一個(gè)最終的promise實(shí)例,當(dāng)所有promise都轉(zhuǎn)為fulfilled時(shí)返回的最終的promise實(shí)例將會(huì)轉(zhuǎn)換為fulfilled,此時(shí)這個(gè)promise的值為傳入的promise的值的集合。而如果傳入的那組promise中有一個(gè)rejected,返回的promise就會(huì)rejected。
Promise.all = function(promises){
return new Promise((resolve,reject)=>{
let result = [],
count = 0;
function done(i,data){
result[i] = data;
if(++count===promises.length){
resolve(result);
}
}
for(let i=0;i<promises.length;++i){
promises[i].then((value)=>{
done(i,value);
},(reason)=>{
reject(reason);
});
}
});
}
Promise.race
也是傳入一組promise返回一個(gè)promise,哪個(gè)promise先轉(zhuǎn)換狀態(tài),就返回這個(gè)promise的結(jié)果
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;++){
promises[i].then(resolve,reject);
}
});
}
Promise.promisify
將一個(gè)異步函數(shù)promise化,使其可以then,可以鏈?zhǔn)綍?shū)寫(xiě)
Promise.promisify = function(fn){
return function(...args){
return new Promise((resolve,reject)=>{
fn.apply(null,[...args,function(err,data){
err?reject(err):resolve(data);
}]);
});
}
}
Promise.promisifyAll
將一個(gè)對(duì)象下的所有方法都promisify化
Promise.promisifyAll = function(obj){
for(var attr in obj){
if(obj.hasOwnProperty(key)&&typeof obj[attr]==='function'){
obj[attr+'Async'] = Promise.promisify(obj[attr]);
}
}
}
測(cè)試
要對(duì)實(shí)現(xiàn)的Promise進(jìn)行測(cè)試,除了實(shí)現(xiàn)t規(guī)范要求then方法和catch方法外還需要先在你的promise下添加一個(gè)方法
Promise.deferred = Promise.defer = function(){
let defer = {};
defer.promise = new Promise(function(resolve,reject){
defer.resolve = resolve;
defer.reject = reject;
});
return defer;
}
然后按下述進(jìn)行測(cè)試
npm i -g promises-aplus-tests
promises-aplus-tests yourFileName.js
實(shí)現(xiàn)代碼【終板】
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function Promise(executor) {
let self = this; //緩存下
self.value = undefined; //用來(lái)存放value和reason,因?yàn)閜romise只會(huì)處于一種狀態(tài)故可只用一個(gè)變量來(lái)表示。
self.status = PENDING; //將初始狀態(tài)設(shè)置為pending
self.onFulfilledCallbacks = []; //用來(lái)存放所有成功的回調(diào)函數(shù)
self.onRejectedCallbacks = []; //用來(lái)存放所有失敗的回調(diào)函數(shù)
try {
executor(resolve, reject); //調(diào)用執(zhí)行函數(shù),將resolve和reject方法作為參數(shù)傳入
} catch (e) {
reject(e); //若執(zhí)行函數(shù)中存在異常直接用拋出的值來(lái)拒絕promise
}
function resolve(value) {
if (value instanceof Promise) { //和resolvePromise有點(diǎn)聯(lián)系的是 當(dāng)then return的promise中又resolve了一個(gè)promise會(huì)先走這,會(huì)將resolve里的promise的值賦給調(diào)用resolve的promise(說(shuō)法欠妥,意會(huì)即可)
return value.then(resolve, reject); //這意味著如果promise1 resolve中是一個(gè)promise2,那么promise1狀態(tài)的改變時(shí)間會(huì)被推遲,直到promise2狀態(tài)改變調(diào)用promise2的回調(diào)時(shí),promise1狀態(tài)才會(huì)改變才會(huì)觸發(fā)promise1的回調(diào)
}
setTimeout(function () {
if (self.status === PENDING) {
self.status = FULFILLED;
self.value = value;
self.onFulfilledCallbacks.forEach(cb => cb(self.value)); //2.2.2. //2.2.6.
}
})
}
function reject(reason) {
setTimeout(function () {
if (self.status === PENDING) {
self.status = REJECTED;
self.value = reason;
self.onRejectedCallbacks.forEach(cb => cb(self.value)); //2.2.3. //2.2.6.
}
})
}
}
Promise.prototype.then = function (onFulfilled, onRejected) { //2.2.1.
//2.2.7.3-2.2.7.4 //2.2.5.
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => {
throw reason
};
let self = this,
promise2; //2.2.7.0 //聲明要返回的promise2
if (self.status === PENDING) {
//2.2.7.
return promise2 = new Promise(function (resolve, reject) {
//存儲(chǔ)then方法綁定的回調(diào)函數(shù) //2.2.6.
self.onFulfilledCallbacks.push((value) => {
try {
let x = onFulfilled(value);
resolvePromise(promise2, x, resolve, reject); //2.2.7.1 //resolve/reject屬于promise2 //若此方法執(zhí)行說(shuō)明promise1狀態(tài)已經(jīng)更改
} catch (e) {
reject(e); //2.2.7.2
}
});
self.onRejectedCallbacks.push((reason) => {
try {
let x = onRejected(reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
} else if (self.status === FULFILLED) {
return promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
} else {
return promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onRejected(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
}
};
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) { //2.3.1.
return reject(new TypeError('禁止循環(huán)引用!'));
}
let called = false;
//2.3.2.
if (x instanceof Promise) {
if (x.status === PENDING) { //2.3.2.1
x.then((y) => {
resolvePromise(promise2, y, resolve, reject); //因?yàn)榇藭r(shí)的y,有可能也是一個(gè)promise //掛上一個(gè)鉤子只要x狀態(tài)轉(zhuǎn)化為成功態(tài)就遞歸調(diào)用resolvePromise
}, reject);
} else { //此分支存在的意義在于若executor調(diào)用resolve/reject不是異步的且不在resolve/reject中設(shè)置setTimeout,意味著當(dāng)new的時(shí)候就會(huì)返回一個(gè)帶狀態(tài)的promise就會(huì)走這里。
x.then(resolve, reject); //2.3.2.2-2.3.2.3 //只要x狀態(tài)改變,就以x的狀態(tài)和值來(lái)改變promise2的狀態(tài)和值 //這個(gè)值可能是一個(gè)promise,前提是在上面那種假設(shè)實(shí)現(xiàn)中 //如果不符合上面那種實(shí)現(xiàn)且不想像規(guī)范一樣允許值可以為一個(gè)promise或則對(duì)象 可除去此分支
}
} else if (x != null && ((typeof x === 'function') || (typeof x === 'object'))) { //2.3.3.
try {
let then = x.then; //2.3.3.1
if (typeof then === 'function') {
//2.3.3.3.
then.call(x, (y) => {
if (called) return; //2.3.3.3.3.
called = true;
resolvePromise(promise2, y, resolve, reject); //在resolve中又包含promise的情況下,由于resolve中的 value.then存在,當(dāng)前回調(diào)調(diào)用時(shí),resolve中的promise狀態(tài)一定已經(jīng)改變,在狀態(tài)已經(jīng)改變的時(shí)候利用then綁定回調(diào),會(huì)走then中的status==fulfilled或則rejected分支
}, (reason) => {
if (called) return;
called = true;
reject(reason);
});
} else {
resolve(x); //2.3.3.4. //1.3
}
} catch (e) {
if (called) return; //2.3.3.3.4.1.
called = true;
reject(e); //2.3.3.2. //2.3.3.3.4.2.
}
} else { //2.3.4.
resolve(x);
}
}
Promise.deferred = Promise.defer = function () {
let defer = {};
defer.promise = new Promise(function (resolve, reject) {
defer.resolve = resolve;
defer.reject = reject;
});
return defer;
};
Promise.prototype.catch = function (onRejected) {
this.then(null, onRejected)
};
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
resolve(value);
})
};
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason);
})
};
Promise.all = function(promises){
return new Promise((resolve,reject)=>{
let result = [];
let count = 0;
function done(i,data){
result[i] = data;
if(++count === promises.length){
resolve(result);
}
}
for(let i=0;i<promises.length;++i){
promises[i].then((value)=>{
done(i,value);
},reject);
}
})
};
Promise.race = function(promises){
return new Promise(function(resolve,reject){
for(let i=0;i<promises.length;++i){
promises[i].then(resolve,reject);
}
});
};
Promise.promisify = function(fn){
return function(...args){
return new Promise((resolve,reject)=>{
fn.apply(null,[...args,function(err,data){
err?reject(err):resolve(data);
}]);
});
}
};
Promise.promisifyALL = function(obj){
for(var key in obj){
if(obj.hasOwnProperty(key)&&typeof obj[key]=='function'){
obj[key+'Async'] = Promise.promisify(obj[key]);
}
}
};
module.exports = Promise;