Promise 必須為三種狀態(tài)之一死嗦, 等待態(tài)Pending
褒繁、執(zhí)行態(tài)Fulfilled
和拒絕態(tài)Rejected
犬庇。一旦Promise被resolve或reject俯渤,便不能再遷移至其他任何狀態(tài)呆细,即狀態(tài)不可改變immutable
。
基本執(zhí)行過程:
- 初始化Promise狀態(tài)(pending)
- 立即執(zhí)行Promise中傳入的Fn函數(shù)八匠,將Promise內(nèi)部resolve絮爷、reject函數(shù)作為參數(shù)傳遞給Fn,然后按照
事件機(jī)制時(shí)機(jī)
處理梨树。 - 執(zhí)行then方法注冊(cè)回調(diào)處理數(shù)據(jù)(then方法可被同一個(gè)promise調(diào)用多次)
- Promise關(guān)鍵是要保證then方法傳入的參數(shù) onFulfilled 和 onRejected, 必須在then方法被調(diào)用的那一輪事件循環(huán)之后的新執(zhí)行棧中執(zhí)行坑夯。
鏈?zhǔn)秸{(diào)用
鏈?zhǔn)絇romise是指在當(dāng)前Promise 達(dá)到fulfilled狀態(tài)后,即開始執(zhí)行下一個(gè)Promise抡四。
- 例子1:
new Promise(function(resolve,reject){
setTimeout(_ => {
resolve(1);
resolve(3);
reject('error');
},1000)
}).then(res => {
console.log(res);
}).then(res => {
console.log(res);
}).then(res => {
console.log(res);
});
// 1
// undefiend
// undefiend
結(jié)果輸出不同的data, 由此可以看出:
- 可以進(jìn)行鏈?zhǔn)秸{(diào)用柜蜈, 一個(gè)Promise可以有多個(gè)then()
- 只輸出第一次resolve的值,reject沒有輸出指巡, 由此可見 Promise的狀態(tài)只可以由
或者
,是不可逆的淑履。
- then中返回了新的Promise, 但是then中注冊(cè)的回調(diào)仍然屬于上一個(gè)Promise的。
- 例子2:
function test() {
return new Promise((resolve, reject) => {
setTimeout(_ => {
resolve(2);
}, 100)
})
}
new Promise((resolve, reject) => {
setTimeout(_ => {
resolve(1);
}, 100);
}).then(res => {
console.log(res);
return test();
}).then(res => {
console.log(res);
});
// 1
// 2
當(dāng)Promise的狀態(tài)變?yōu)閒ulfilled之后會(huì)執(zhí)行其回調(diào)函數(shù)厌处, 而回調(diào)函數(shù)里返回下一個(gè)Promise,并且fulfilled時(shí)鳖谈,然后執(zhí)行其回調(diào)岁疼, 以此類推下去... 鏈?zhǔn)秸{(diào)用的效果就出來(lái)了阔涉。
上例中, test里面創(chuàng)建的Promise捷绒,它是沒有調(diào)用then方法的瑰排。 所上分析得出Promise的回調(diào)函數(shù)是通過調(diào)用其then方法注冊(cè)的, 因此test方法創(chuàng)建的Promise其回調(diào)函數(shù)為空暖侨。顯然如果沒有回調(diào)函數(shù)椭住,執(zhí)行resolve的時(shí)候, 是沒辦法鏈?zhǔn)秸{(diào)用的字逗, 因此京郑, 我們需要主動(dòng)為其注入回調(diào)函數(shù)。
異常處理
異常通常是指在執(zhí)行成功或失敗回調(diào)時(shí)代碼出錯(cuò)產(chǎn)生的錯(cuò)誤葫掉, 對(duì)于這類異常些举, 使用 catch 來(lái)捕獲錯(cuò)誤, 并將Promise設(shè)為rejected狀態(tài)即可俭厚。
new Promise((resolve, reject) => {
setTimeout(_ => {
resolve(1);
}, 100);
}).then(res => {
console.log(res);
}).catch(err => {
console.log('err:'+ err);
});
resolve 方法和reject方法
實(shí)際應(yīng)用中户魏, 我們可以使用Promise.resolve 和 Promise.reject方法, 用于將非Promise實(shí)例包裝為Promise實(shí)例。
Promise.resolve('success');
Promise.reject('error');
//等同于
new Promise(resolve => resolve('success'));
new Promise((resolve,reject) => reject('error'));
這些情況下叼丑, Promise.resolve的入?yún)⒖赡苡幸幌聨追N情況:
- 無(wú)參數(shù)(直接返回一個(gè)resolved狀態(tài)的Promise對(duì)象)
- 普通數(shù)據(jù)對(duì)象 (直接返回一個(gè)resolve狀態(tài)的Promise對(duì)象)
- 一個(gè)Promise實(shí)例 (直接返回當(dāng)前實(shí)例)
- 一個(gè)thenable對(duì)象关翎,thenable對(duì)象是具有then方法的對(duì)象(轉(zhuǎn)化為Promise對(duì)象,并立即執(zhí)行thenable對(duì)象的then方法)
Promise.all
Promise.all 參數(shù)是一個(gè)Promise的數(shù)組鸠信, 只有當(dāng)所有的Promise的狀態(tài)都轉(zhuǎn)為fulfilled,才會(huì)執(zhí)行then方法纵寝, 當(dāng)有一個(gè)失敗,都會(huì)進(jìn)入catch被捕獲錯(cuò)誤。Promise.all執(zhí)行成功返回的結(jié)果是每個(gè)Promise返回結(jié)果組成的數(shù)組星立,如果失敗則返回第一個(gè)reject的結(jié)果店雅。
Promise.all會(huì)阻塞一起執(zhí)行的Promise,因此需在必要的業(yè)務(wù)場(chǎng)景來(lái)使用
function test() {
return new Promise((resolve, reject) => {
setTimeout(_ => {
resolve(1);
},100)
})
}
function test2() {
return new Promise((resolve, reject) => {
setTimeout(_ => {
resolve(2)
},1)
})
}
function test3() {
return new Promise((resolve, reject) => {
setTimeout(_ => {
resolve(3);
},300)
})
}
Promise.all([test(),test2(),test3()]).then(res => {
console.log(res); // [1,2,3]
})
入?yún)⑹且粋€(gè)Promise的實(shí)例數(shù)組, 然后注冊(cè)一個(gè)then方法贞铣,然后是數(shù)組中的Promise實(shí)例的狀態(tài)都轉(zhuǎn)為fulfilled之后執(zhí)行then方法闹啦。 這里主要就是一個(gè)計(jì)數(shù)邏輯, 每當(dāng)一個(gè)Promise的狀態(tài)變?yōu)閒ulfilled之后就保存該實(shí)例返回的數(shù)據(jù)辕坝,然后將計(jì)數(shù)減一窍奋, 當(dāng)計(jì)數(shù)器變?yōu)?的時(shí)候, 代表數(shù)組中所有的Promise實(shí)例都執(zhí)行完畢
Promise.race
Promise.race也會(huì)調(diào)用所有的Promise實(shí)例酱畅, 其和Promise.all的不同點(diǎn)是琳袄, 返回結(jié)果是所有Promise實(shí)例最先執(zhí)行完的那個(gè)Promise的結(jié)果,不關(guān)心成功還是失敗纺酸。
function test() {
return new Promise((resolve, reject) => {
setTimeout(_ => {
resolve(1);
},100)
})
}
function test2() {
return new Promise((resolve, reject) => {
setTimeout(_ => {
resolve(2)
},1)
})
}
function test3() {
return new Promise((resolve, reject) => {
setTimeout(_ => {
resolve(3);
},300)
})
}
Promise.race([test(),test2(),test3()]).then(res => {
console.log(res); // 2
})
有了Promise.all的理解窖逗, Promise.race理解起來(lái)就更容易了。它的入?yún)⒁彩且粋€(gè)Promise實(shí)例數(shù)組餐蔬, 然后其then注冊(cè)的回調(diào)方法是數(shù)組中的某一個(gè)Promise的狀態(tài)為fulfilled的時(shí)候執(zhí)行碎紊。因?yàn)镻romise的狀態(tài)只能改變一次,那么我們只需要把Promise.race中產(chǎn)生的Promise對(duì)象的resolve方法樊诺,注入到數(shù)組中的每一個(gè)Promise實(shí)例中的回調(diào)函數(shù)中即可仗考。
雖然then普遍被認(rèn)為是微任務(wù)。 但是瀏覽器沒辦法模擬微任務(wù)词爬, 目前使用setTimeout替代秃嗜, promise的polyfill(es6-promise)里用的也是setTimeout。因此這里直接使用setTimeout,以宏任務(wù)代替了微任務(wù)顿膨。