
原文鏈接
Promise簡(jiǎn)介
ECMAscript6原生提供了Promise對(duì)象,由瀏覽器直接支持,目前大多數(shù)瀏覽器都已經(jīng)實(shí)現(xiàn)了,低版本瀏覽器可以使用es6-promise庫(kù)來(lái)填平兼容性問(wèn)題。Promise最大的好處是把執(zhí)行代碼和處理代碼分離開(kāi),使異步操作邏輯更加清晰。
Promise特點(diǎn)
1、對(duì)象的狀態(tài)不受外界影響
Promise對(duì)象代表一個(gè)異步操作,有三種狀態(tài):
- pending - 初始狀態(tài)。
- fulfilled - 操作成功完成。
- rejected - 操作失敗。
只有異步操作的結(jié)果可以決定當(dāng)前是哪一種狀態(tài),其他操作都不會(huì)影響狀態(tài)改變,這也是Promise最本質(zhì)的特性,對(duì)于調(diào)用者的一種承諾
2、一旦狀態(tài)改變,就不會(huì)再變
Promise對(duì)象的狀態(tài)改變,只有兩種情況:從Pending變?yōu)镽esolved和從Pending變?yōu)镽ejected。只要這兩種情況發(fā)生,狀態(tài)就會(huì)固定,不會(huì)再變了,會(huì)一直保持這個(gè)結(jié)果,與事件不同的是,就算改變已經(jīng)發(fā)生了,再對(duì)Promise對(duì)象添加回調(diào)函數(shù),也會(huì)立即得到這個(gè)結(jié)果,而事件一旦錯(cuò)過(guò)再去監(jiān)聽(tīng),就不會(huì)得到結(jié)果。
Promise優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 有了Promise對(duì)象,就可以將異步操作以同步操作的流程表達(dá)出來(lái),避免了層層嵌套的回調(diào)函數(shù)Promise
- 對(duì)象提供統(tǒng)一的接口,使得控制異步操作更加容易
缺點(diǎn):
- 無(wú)法取消 Promise,一旦新建它就會(huì)立即執(zhí)行,無(wú)法中途取消
- 如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯(cuò)誤,不會(huì)反應(yīng)到外部
- 當(dāng)處于Pending狀態(tài)時(shí),無(wú)法得知目前進(jìn)展到哪一個(gè)階段(剛剛開(kāi)始或者即將完成)
Promise.prototype.then
Promise.prototype.then方法返回的是一個(gè)新的Promise對(duì)象,因此可以采用鏈?zhǔn)綄?xiě)法
ajax("http://www.bestvist.com").then((json) => {
return json.post;
}).then((post) => {
// post處理
});
代碼中使用then方法,指定了兩個(gè)回調(diào)函數(shù)。第一個(gè)回調(diào)函數(shù)完成后,會(huì)將返回結(jié)果作為參數(shù),傳入第二個(gè)then中的回調(diào)函數(shù)執(zhí)行。
ajax("http://www.bestvist.com").then((jsonURL) => {
return ajax(jsonURL);
}).then((post) => {
// post處理
});
如果第一個(gè)回調(diào)函數(shù)返回的是Promise對(duì)象,后一個(gè)回調(diào)函數(shù)會(huì)等待該P(yáng)romise對(duì)象的運(yùn)行結(jié)果,等Promise運(yùn)行結(jié)果返回,再進(jìn)一步調(diào)用。
這種設(shè)計(jì)使得嵌套的異步操作,可以被很容易得改寫(xiě),把回調(diào)函數(shù)的"橫向發(fā)展"改為了"向下發(fā)展"。
Promise.prototype.catch
Promise.prototype.catch錯(cuò)誤捕捉方法是Promise.prototype.then(null, rejection)的別名,用來(lái)指定發(fā)生錯(cuò)誤時(shí)的處理函數(shù)。
ajax("http://www.bestvist.com").then((post) => {
throw Error();
}).catch((error) => {
// 捕捉回調(diào)函數(shù)運(yùn)行時(shí)發(fā)生的錯(cuò)誤進(jìn)行處理
console.log('error:' + error);
});
Promise對(duì)象的錯(cuò)誤具有"冒泡"性質(zhì),會(huì)一直向后傳遞,直到被捕獲為止。
ajax("http://www.bestvist.com").then((jsonURL) => {
return ajax(jsonURL);
}).then((comments) => {
throw Error();
}).catch((error) => {
// 處理前兩個(gè)回調(diào)函數(shù)的錯(cuò)誤
console.log('error:' + error);
});
Promise.resolve
Promise.resolve方法可以將現(xiàn)有對(duì)象轉(zhuǎn)為Promise對(duì)象。如果Promise.resolve方法的參數(shù),不是thenable 對(duì)象(具有then方法的對(duì)象 ),則返回一個(gè)新的Promise對(duì)象,且它的狀態(tài)為fulfilled。
const resolve = Promise.resolve('promise resolve');
resolve.then((s)=>{
console.log(s)
});
輸出結(jié)果:
promise resolve
如果Promise對(duì)象的實(shí)例狀態(tài)為fulfilled,回調(diào)函數(shù)會(huì)立即執(zhí)行,Promise.resolve方法的參數(shù)就是回調(diào)函數(shù)的參數(shù)。
如果Promise.resolve方法的參數(shù)是一個(gè)Promise對(duì)象的實(shí)例,則會(huì)返回該P(yáng)romise實(shí)例。
Promise.reject
Promise.reject(reason)方法與resolve方法類似,也會(huì)返回一個(gè)新的Promise實(shí)例,但該實(shí)例的狀態(tài)為rejected。Promise.reject方法的參數(shù),會(huì)被傳遞給實(shí)例的回調(diào)函數(shù)。
const reject = Promise.reject('promise reject');
reject.then(null, (err) => {
console.log(err)
});
輸出結(jié)果:
promise reject
Promise.all
Promise.all方法用于將多個(gè)Promise實(shí)例,包裝成一個(gè)新的Promise實(shí)例,該方法一般接受一個(gè)數(shù)組作為參數(shù),但不一定是數(shù)組,只要具有iterator接口。且返回的每個(gè)成員都是Promise實(shí)例。
const getRandom = () => +(Math.random()*1000).toFixed(0);
const ajax = (taskID) => new Promise(resolve => {
let timeout = getRandom();
console.log(`taskID=${taskID} start.`);
setTimeout(function() {
console.log(`taskID=${taskID} finished in time=${timeout}.`);
resolve(taskID)
}, timeout);
});
Promise.all([ajax(1),ajax(2),ajax(3)])
.then(resultList => {
console.log('results:',resultList);
});
輸出結(jié)果:
taskID=1 start.
taskID=2 start.
taskID=3 start.
taskID=2 finished in time=27.
taskID=3 finished in time=257.
taskID=1 finished in time=876.
results: [1, 2, 3]
Promise.all狀態(tài)分為兩種:
- 只有ajax(1)、ajax(2)、ajax(3)的狀態(tài)都變成fulfilled,返回的狀態(tài)才會(huì)變成fulfilled,此時(shí)ajax(1)、ajax(2)、ajax(3)的返回值組成一個(gè)數(shù)組,傳遞給Promise.all的回調(diào)函數(shù)。
- 只要ajax(1)、ajax(2)、ajax(3)之中有一個(gè)是rejected,返回的狀態(tài)就變成rejected,此時(shí)第一個(gè)被reject的實(shí)例的返回值,會(huì)傳遞給Promise.all的回調(diào)函數(shù)。
Promise.race
Promise.race方法也是將多個(gè)Promise實(shí)例,包裝成一個(gè)新的Promise實(shí)例,與Promise.all不同的是一旦有狀態(tài)改變,就會(huì)返回第一個(gè)狀態(tài)改變的Promise實(shí)例返回值。
const getRandom = () => +(Math.random()*1000).toFixed(0);
const ajax = (taskID) => new Promise(resolve => {
let timeout = getRandom();
console.log(`taskID=${taskID} start.`);
setTimeout(function() {
console.log(`taskID=${taskID} finished in time=${timeout}.`);
resolve(taskID)
}, timeout);
});
Promise.race([ajax(1),ajax(2),ajax(3)])
.then(result => {
console.log('results:',result);
});
輸出結(jié)果:
taskID=1 start.
taskID=2 start.
taskID=3 start.
taskID=2 finished in time=59.
results: 2
taskID=3 finished in time=707.
taskID=1 finished in time=854.
Promise.race雖然返回第一個(gè)狀態(tài)改變的Promise實(shí)例,但不能阻止其他Promise實(shí)例狀態(tài)改變。
如果Promise.all方法和Promise.race方法的參數(shù),不是Promise實(shí)例,就會(huì)調(diào)用Promise.resolve方法,將參數(shù)轉(zhuǎn)為Promise實(shí)例,再進(jìn)一步處理。