JavaScript里通常不建議阻塞主程序,尤其是一些代價(jià)比較昂貴的操作速种,如查找數(shù)據(jù)庫姜盈,下載文件等操作,應(yīng)該用異步API配阵,如Ajax模式馏颂。在執(zhí)行操作的的同時(shí)示血,主程序能繼續(xù)處理后序代碼,等操作結(jié)束后調(diào)用回調(diào)函數(shù)救拉。
例如文件下載矾芙,同步方式的話,萬一網(wǎng)絡(luò)情緒不穩(wěn)定近上,下載文件耗時(shí)很長剔宪,頁面就卡住了,所以一定會用異步API:
downloadAsync("http://example.com/file.txt", function(text) {
console.log(text);
});
但通常異步操作并非只有單一步驟壹无,就以文件下載為例葱绒,如果下載3個(gè)有關(guān)聯(lián)的文件,你要如何才能保證依次下載呢斗锭?你可能會用回調(diào)嵌套:
downloadAsync("a.txt", function(a) {
downloadAsync("b.txt", function(b) {
downloadAsync("c.txt", function(c) {
console.log("Contents: " + a + b + c);
}, function(error) {
console.log("Error: " + error);
});
}, function(error) {
console.log("Error: " + error);
});
}, function(error) {
console.log("Error: " + error);
});
回調(diào)嵌套本身沒有什么錯(cuò)地淀。它被詬病之處在于,這才3層岖是,基本框架就很難看了帮毁。業(yè)務(wù)邏輯稍微多一點(diǎn),即便你努力重構(gòu)出子函數(shù)豺撑,也難以避免讓主體框架陷入“回調(diào)地獄”烈疚,代碼會難以閱讀和維護(hù)。
這就是Promise誕生的原因聪轿,它并不是全新的數(shù)據(jù)類型爷肝,而是一種組織代碼的方式,內(nèi)部改進(jìn)了回調(diào)函數(shù)陆错,對外暴露出then方法灯抛。讓開發(fā)者避免陷入“回調(diào)地獄”,寫出更加清晰的音瓷,類似函數(shù)鏈的代碼对嚼。ES6將Promise納入了標(biāo)準(zhǔn),本篇就介紹一下Promise绳慎。
- Promise對象
- then方法
- catch方法
- 靜態(tài)方法(resolve纵竖,reject,all偷线,race)
- 例子
Promise對象
Promise既然是一種代碼組織方式磨确,它就需要為用戶提供構(gòu)造函數(shù)來創(chuàng)建一個(gè)Promise對象。構(gòu)造函數(shù)原型:new Promise(function(resolve, reject) { … } );声邦。參照MDN
構(gòu)造函數(shù)用一個(gè)函數(shù)作為參數(shù)乏奥,該函數(shù)有兩個(gè)參數(shù),兩個(gè)參數(shù)均是回調(diào)函數(shù)亥曹,由JS引擎提供邓了,你不用自己部署了恨诱。第一個(gè)參數(shù)resolve,當(dāng)異步操作成功時(shí)會調(diào)用骗炉,它有一個(gè)參數(shù)用于傳遞異步操作成功的結(jié)果照宝。第二個(gè)參數(shù)reject,當(dāng)異步操作失敗時(shí)會調(diào)用句葵,它有一個(gè)參數(shù)用于傳遞異步操作失敗的信息厕鹃。例如:
var myPromise = new Promise(function(resolve, reject) {
... //異步操作
if( success ) {
resolve(value);
} else {
reject(error);
}
});
上面生成了一個(gè)Promise對象,它代表著一個(gè)異步操作乍丈。有3種狀態(tài)Pending剂碴,Resolved(MDN里又稱Fulfilled),Rejected轻专∫涿看名字就知道分別表示異步操作中,操作成功请垛,操作失敗催训。
一旦調(diào)用構(gòu)造函數(shù)開始生成Promise對象(如上面myPromise),就會立即執(zhí)行異步操作宗收。異步操作成功的話漫拭,myPromise狀態(tài)會從Pending切換成Resolved。失敗的話镜雨,狀態(tài)從Pending切換成Rejected儿捧。狀態(tài)改變后就固定了颓影,永遠(yuǎn)不會再次改變临谱,這就是Promise的語義城豁,表示“承諾”。一旦承諾,盒突恚枯石爛都不會變驼壶。實(shí)例對象生成后,就能調(diào)用then方法了碟渺。
then方法
Promise.prototype.then()方法顯然就是Promise的精華绒极。函數(shù)聲明:p.then(resolve, reject);
。參照MDN
注意它不是靜態(tài)方法审丘,需要經(jīng)由Promise實(shí)例對象來調(diào)用喉镰。
then方法有兩個(gè)參數(shù),第一個(gè)參數(shù)是Promise實(shí)例對象為Resolved狀態(tài)時(shí)的回調(diào)函數(shù),它的參數(shù)就是上面Promise構(gòu)造函數(shù)里resolve傳遞過來的異步操作成功的結(jié)果昼牛。
第二個(gè)參數(shù)可選辜伟,是Promise實(shí)例對象為Rejected狀態(tài)時(shí)的回調(diào)函數(shù)尝蠕,它的參數(shù)就是上面Promise構(gòu)造函數(shù)里reject傳遞過來的異步操作失敗的信息。
例如:
var myPromise = new Promise(function(resolve, reject) {
console.log("執(zhí)行異步操作");
resolve("成功");
//reject ("失敗啦");
});
console.log("后續(xù)操作");
myPromise.then(function(value) {
console.log(value);
}, function(error) {
console.log(error);
});
//執(zhí)行異步操作
//后續(xù)操作
//成功
上面結(jié)果可以看出星压,一旦調(diào)用構(gòu)造函數(shù)開始生成Promise對象竣贪,就會立即執(zhí)行異步操作甘桑,打印出第一行l(wèi)og艘蹋。由于是異步票灰,會繼續(xù)執(zhí)行生成Promise對象外的代碼女阀,打印出第二行l(wèi)og。異步操作成功后屑迂,打印出then里resolve回調(diào)函數(shù)里的log浸策。
由于通常異常會用下面介紹的catch方法捕捉,因此then方法的第二個(gè)參數(shù)通常會省略:
myPromise.then(function(value) {
... //成功
}).catch(function(error) {
... //失敗
});
then方法最強(qiáng)大之處在于惹盼,它內(nèi)部可以使用return或throw來實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用庸汗。使用return或throw后的返回值是一個(gè)新的Promise實(shí)例對象(注意,不是原來那個(gè)Promise實(shí)例對象):
var myPromise = new Promise(function(resolve, reject) {
resolve(1);
});
myPromise.then(function(value) {
console.log("第" + value + "一次異步操作成功"); //第1次異步操作成功
return value+1;
}).then(function(value) {
console.log("第" + value + "一次異步操作成功"); //第2次異步操作成功
});
myPromise.then(function(value) {
console.log("第" + value + "一次異步操作成功"); //第1次異步操作成功
});
上面代碼中手报,myPromise對象第一次調(diào)用then時(shí)蚯舱,value為1,打印出log后return出一個(gè)匿名Promise對象掩蛤。
你可能會疑惑枉昏,看代碼return value+1;
(相當(dāng)于return 2;
)只是返回了一個(gè)數(shù)字,并沒有返回Promise對象呀揍鸟。其實(shí)它會隱式調(diào)用下面介紹的Promise.resolve靜態(tài)方法將轉(zhuǎn)為一個(gè)Promise對象兄裂。具體怎么轉(zhuǎn)的,請參照下面介紹的Promise.resolve靜態(tài)方法。
如果then方法內(nèi)部不是return晰奖,而是throw谈撒,會隱式調(diào)用下面介紹的Promise.reject靜態(tài)方法返回一個(gè)Rejected狀態(tài)的Promise對象。
由于then里用return或throw返回的是另一個(gè)Promise對象匾南,這就實(shí)現(xiàn)了鏈?zhǔn)秸{(diào)用啃匿。
代碼中返回得到的匿名Promise對象,由于狀態(tài)是Resolved午衰,立即調(diào)用鏈?zhǔn)街械诙€(gè)then方法立宜,value為2。(如果匿名Pormise對象里有異步API臊岸,那仍舊會等操作成功或失敗后橙数,再觸發(fā)調(diào)用鏈中的then方法)
最后一次then方法由myPromise對象調(diào)用,因?yàn)镻romise對象的狀態(tài)改變后就固定了帅戒,永遠(yuǎn)不會再次改變灯帮,所以value值仍舊為1。
一個(gè)常見的錯(cuò)誤是逻住,忘記寫return或throw钟哥,但卻對then方法使用鏈?zhǔn)秸{(diào)用。如上例中將return語句刪掉:
myPromise.then(function(value) {
console.log("第" + value + "一次異步操作成功"); //第1次異步操作成功
}).then(function(value) {
console.log("第" + value + "一次異步操作成功"); //第undefined次異步操作成功
});
結(jié)果發(fā)現(xiàn)鏈?zhǔn)秸{(diào)用后瞎访,下一個(gè)then方法得到的是undefined腻贰。因?yàn)槿绻伙@式寫return語句的話,JS里的函數(shù)會自動 return undefined扒秸。這樣就相當(dāng)于調(diào)用下面介紹的Promise.resolve(undefined)播演。雖然瀏覽器覺得這段代碼合法,不會報(bào)錯(cuò)伴奥,但通常來說這不是你期待的結(jié)果写烤。因此推薦:
確保處于調(diào)用鏈中間的then方法內(nèi)部永遠(yuǎn)顯式的調(diào)用return或者throw。
catch方法
Promise.prototype.catch()同樣是實(shí)例方法拾徙,需要經(jīng)由Promise實(shí)例對象來調(diào)用洲炊,用于Promise實(shí)例對象狀態(tài)為Rejected的后續(xù)處理,即異常處理尼啡。函數(shù)聲明:p.catch(reject);
暂衡。參照MDN
catch方法本質(zhì)上等價(jià)于then(null, reject),因此參數(shù)reject在上面介紹過了玄叠,是一個(gè)回調(diào)函數(shù)古徒,它的參數(shù)就是Promise對象狀態(tài)變?yōu)镽ejected后,傳遞來的錯(cuò)誤信息读恃。
例如:
var myPromise = new Promise(function(resolve, reject) {
throw "異步操作失敗";
resolve("成功");
});
myPromise.then(function(value) {
console.log(value);
}).catch(function(e) {
console.log(e);
});
//異步操作失敗
異步操作時(shí)throw了異常,導(dǎo)致myPromise狀態(tài)變成Rejected。由于then里未定義第二個(gè)可選的reject回調(diào)函數(shù)寺惫,所以跳過then方法疹吃,進(jìn)入catch,打印出log西雀。
有個(gè)細(xì)節(jié)要注意萨驶,上面異步操作里throw和resolve語句別寫反了,寫反了是不會捕捉異常的:
var myPromise = new Promise(function(resolve, reject) {
resolve("成功"); //throw和resovle的順序?qū)懛戳? throw "異步操作失敗";
});
myPromise.then(function(value) {
console.log(value);
}).catch(function(e) {
console.log(e);
});
//成功
上面代碼未能捕獲異常艇肴,并不是throw語句未被執(zhí)行腔呜,throw語句確實(shí)被執(zhí)行了。但由于已經(jīng)將狀態(tài)改為Resolved了再悼,Promise對象的狀態(tài)一旦改變將永不再變核畴。因此不會進(jìn)入catch語句里。即throw異常的話冲九,要保證Promise對象的狀態(tài)為Rejected谤草,否則即使throw了異常,也沒有回調(diào)函數(shù)能捕捉該異常莺奸。
那如果catch之前的then里也定義了第二個(gè)可選的reject回調(diào)函數(shù)參數(shù)呢丑孩?究竟是then的reject捕捉還是catch捕捉?其實(shí)明白了catch是then(null, reject)的別名灭贷,就能推導(dǎo)出温学,應(yīng)該被then的reject捕捉:
myPromise.then(function(value) {
console.log(value);
},function(e) {
console.log(e + ",由then的reject回調(diào)函數(shù)捕捉");
}).catch(function(e) {
console.log(e);
});
//異步操作失敗甚疟,由then的reject回調(diào)函數(shù)捕捉
上面代碼等價(jià)于:
myPromise.then(function(value) {
console.log(value);
},function(e) {
console.log(e + "仗岖,由then的reject回調(diào)函數(shù)捕捉");
}).then(null, function(e) {
console.log(e);
});
//異步操作失敗,由then的reject回調(diào)函數(shù)捕捉
從結(jié)果可以看出古拴,catch等價(jià)于then(null, reject)箩帚,相當(dāng)于調(diào)用鏈上全是then,自然是被第一個(gè)定義了reject回調(diào)的then捕捉黄痪。
當(dāng)然使用catch看起來代碼更清晰紧帕,所以通常會省略then方法的第二個(gè)參數(shù)reject回調(diào),用catch來捕捉異常桅打。
then的返回值是一個(gè)全新的Promise對象是嗜,catch也不例外,同樣適用于鏈?zhǔn)秸{(diào)用挺尾。雖然通常catch都放在鏈?zhǔn)秸{(diào)用的最后鹅搪,但沒人強(qiáng)制規(guī)定必須這么做,catch后面完全可以繼續(xù)接then遭铺。同樣的丽柿,如果catch未放在調(diào)用鏈最后恢准,而是在調(diào)用鏈中間的話,別忘了顯示地寫上return或throw語句甫题。
另外Promise對象的異常同樣具有冒泡特性馁筐,會一直向后傳遞,直到被捕捉到為止坠非。因此then方法中拋出的異常敏沉,也能被catch捕捉。因?yàn)檎{(diào)用鏈里then拋出異常相當(dāng)于將匿名Promise對象的狀態(tài)改成Rejected炎码,這樣異常在調(diào)用鏈上傳遞盟迟,直到異常被捕捉到:
var myPromise = new Promise(function(resolve, reject) {
resolve("成功");
});
myPromise.then(function(value) {
console.log(value);
throw "then的resolve里拋出異常";
}).catch(function(e) {
console.log(e);
});
//成功
//then的resolve里拋出異常
上面代碼中,異常操作成功潦闲,打印出log攒菠。但在then的resolve回調(diào)函數(shù)里又throw出了異常,該異常會被then返回的匿名Promise對象繼續(xù)傳遞給catch矫钓。如果catch之前有多個(gè)then要尔,無論哪里拋出異常,最終都會被catch捕捉到新娜。
那如果異常未被捕捉到會怎么樣呢赵辕?例如myPromise.then(…).catch(…).then(…);。最后的then里拋出異常概龄,或者catch里拋出異常(沒錯(cuò)还惠,catch等價(jià)于then(null, reject),自然在catch方法之中私杜,也能再拋出異常)蚕键,會怎么樣呢?
這就是Promise異常的冒泡和傳統(tǒng)的try/catch代碼塊的異常冒泡的區(qū)別:如果Promise異常最終未被捕捉到衰粹,不會繼續(xù)傳遞到外層代碼锣光,即不會有任何反應(yīng):
var myPromise = new Promise(function(resolve, reject) {
resolve(x); //error,x未定義
});
myPromise.then(function(e) {
console.log('成功');}
);
上面代碼沒有catch語句铝耻,因此異常未被捕捉到誊爹,外層代碼也沒有任何反應(yīng),仿佛什么都沒發(fā)生(注意瓢捉,試下來Chrome會拋出x未定義的錯(cuò)誤)频丘。忘記在最后加上catch,會導(dǎo)致珍貴的異常信息丟失泡态,這對調(diào)試來說是異常痛苦的經(jīng)歷搂漠。要記住,通常程序員對自己寫的代碼會很自信某弦,覺得不會拋出一個(gè) error桐汤,但現(xiàn)實(shí)不同環(huán)境下而克,相同代碼的執(zhí)行結(jié)果會大相徑庭。所以為了將來調(diào)試方便惊科,建議:
在promise調(diào)用鏈最后添加catch拍摇。
myPromise().then(function () {
…
}).then(function () {
…
}).catch(console.log.bind(console));
靜態(tài)方法(resolve亮钦,reject馆截,all,race)
這里介紹的4個(gè)都是靜態(tài)方法蜂莉,非實(shí)例方法蜡娶,用Promise對象是無法調(diào)用的。
Promise.resolve將對象轉(zhuǎn)為Promise對象映穗。函數(shù)聲明:Promise.resolve(value);
窖张。參照MDN
參數(shù)是一個(gè)對象,轉(zhuǎn)換成Promise對象返回出去蚁滋。你可以用該方法將jQuery的Deferred對象轉(zhuǎn)換成一個(gè)Promise對象:
var myPromise = Promise.resolve($.ajax('/xx.json'));
參數(shù)分4種情況:
1.如果參數(shù)對象本身就是Promise對象宿接,那就將該對象原封不動返回。
2.如果參數(shù)對象本身是thenable對象(即對象有then方法)辕录,轉(zhuǎn)為Promise對象并返回后睦霎,立即執(zhí)行thenable對象的then方法。例如:
var myPromise = Promise.resolve({
then: function(resolve) {
resolve("成功");
}
});
console.log(myPromise instanceof Promise);
myPromise.then(function(v) {
console.log(v);
});
//true
//成功
上面代碼中走诞,Promise.resolve的參數(shù)里有then方法副女,因此myPromise對象生成后,立即執(zhí)行then方法蚣旱,根據(jù)異步操作的結(jié)果碑幅,調(diào)用then里resolve/reject回調(diào)函數(shù)
3.如果參數(shù)對象本身不是thenable對象(即對象沒有then方法),例如一個(gè)數(shù)字?jǐn)?shù)組等塞绿,那會返回一個(gè)狀態(tài)固定為Resolved的全新的Promise對象:
var myPromise = Promise.resolve([1,2,3]);
myPromise.then(function(v) {
console.log(v);
});
//[1,2,3]
由于數(shù)組不是具有then方法的對象沟涨,返回Promise實(shí)例的狀態(tài)固定為Resolved,觸發(fā)調(diào)用then方法异吻。原先的參數(shù)對象裹赴,會被傳給then方法的resolve回調(diào)函數(shù)做為參數(shù)。
為什么呢涧黄?因?yàn)镻romise.resolve([1,2,3]);等價(jià)于:
new Promise(function(resolve, reject) {
resolve([1,2,3]);
});
4.如果沒有參數(shù)顿肺,那會返回一個(gè)狀態(tài)固定為Resolved的全新的空的Promise對象授滓。雖然瀏覽器不會報(bào)錯(cuò),但也沒什么卵用。
Promise.reject將對象轉(zhuǎn)換成一個(gè)狀態(tài)為Rejected的全新的Promise對象躯肌。其他同Promise.resolve,不贅述纫骑。函數(shù)聲明:Promise.reject(error);
。參照MDN
該方法常用于調(diào)試嫉你,顯示棧信息:
Promise.reject(new Error("fail")).then(function(error) {
// not called
}, function(error) {
console.log(error); // Stacktrace
});
Promise.all方法用于將多個(gè)Promise實(shí)例對象,包裝成一個(gè)新的Promise實(shí)例對象躏惋。函數(shù)聲明:Promise.all(iterable);
幽污。參照MDN
參數(shù)是一個(gè)Promise對象數(shù)組。例如:
var p = Promise.all([p1, p2, p3]);
p1簿姨,p2距误,p3都是Promise對象,如果不是扁位,就會先調(diào)用Promise.resolve方法准潭,將參數(shù)轉(zhuǎn)為Promise對象。(Promise.all方法的參數(shù)可以不是數(shù)組域仇,但必須具有Iterator接口)
返回值是一個(gè)全新的Promise對象刑然。它的狀態(tài)由參數(shù)的狀態(tài)決定:
當(dāng)p1,p2暇务,p3的狀態(tài)都變成Resolved泼掠,p的狀態(tài)才會變成Resolved。然后p1垦细,p2择镇,p3的返回值組成一個(gè)數(shù)組,傳遞給p的回調(diào)函數(shù)蝠检。
只要p1沐鼠,p2,p3中有一個(gè)變成Rejected叹谁,p的狀態(tài)就變成Rejected饲梭,此時(shí)第一個(gè)Rejected的實(shí)例的返回值,會傳遞給p的回調(diào)函數(shù)焰檩。
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "foo");
});
Promise.all([p1, p2, p3]).then(values => {
console.log(values); //1秒后顯示[3, 1337, "foo"]
});
all方法用于哪里呢憔涉?一個(gè)非常常見場景是:程序員想用forEach,for析苫,while等循環(huán)來處理他們的異步結(jié)果兜叨。例如刪除所有臨時(shí)文件:
db.allDocs({include_docs: true}).then(function (result) {
result.rows.forEach(function (row) { //用forEach刪除所有臨時(shí)文件
db.remove(row.doc);
});
}).then(function () {
//我認(rèn)為執(zhí)行到這里時(shí),所有臨時(shí)文件都已經(jīng)被刪了衩侥。但是我錯(cuò)了…
});
上面代碼国旷,第一個(gè)then的回調(diào)里,期望用循環(huán)刪除所有臨時(shí)文件后茫死,再進(jìn)入第二個(gè)then跪但。但事實(shí)上,由于第一個(gè)then里沒有return語句(原因是刪除文件是個(gè)異步動作峦萎,如果寫了return語句屡久,刪文件執(zhí)行之前就會return)忆首,所以返回的是undefined。因此第二個(gè)then并不是在所有文件均被刪除后才執(zhí)行的被环,實(shí)際上糙及,第二個(gè)then的執(zhí)行不會有任何延遲,會被立即執(zhí)行筛欢。
如果第二個(gè)then里并不需要對臨時(shí)文件進(jìn)行任何操作浸锨,那這個(gè)bug可能會隱藏的很深。但如果在第二個(gè)then里刷新UI頁面悴能,那時(shí)不時(shí)UI頁面上會出現(xiàn)一些還來不及刪掉的文件揣钦,這取決于刪文件的速度。程序員都知道漠酿,最討厭的不是出bug,而是出了看運(yùn)氣才能再現(xiàn)的bug谎亩。
所以應(yīng)該用Promise.all來寫這種需要循環(huán)處理異步的操作炒嘲,可以理解為異步的循環(huán)。
db.allDocs({include_docs: true}).then(function (result) {
return Promise.all(result.rows.map(function (row) {
return db.remove(row.doc);
}));
}).then(function (arrayObject) {
//我承諾執(zhí)行到這里時(shí)匈庭,所有臨時(shí)文件都已經(jīng)被刪掉了夫凸。
})
Promise.race方法和all方法類似,函數(shù)聲明:Promise.race(iterable);
阱持,參照MDN
var p = Promise.race([p1,p2,p3]);
參數(shù)p1夭拌,p2,p3都是Promise對象衷咽,如果不是鸽扁,就會先調(diào)用Promise.resolve方法,將參數(shù)轉(zhuǎn)為Promise對象镶骗,這點(diǎn)和all相同桶现,不贅述。
和Promise.all的區(qū)別是鼎姊,race表示競爭骡和。只要任意一個(gè)參數(shù)對象的狀態(tài)發(fā)生改變,就會立即返回一個(gè)相同狀態(tài)的Promise對象相寇。
簡單地說:all里全為Resolved才返回Resolved慰于,有一個(gè)為Rejected就返回Rejected。race里有一個(gè)為Resolved/Rejected就返回Resolved/Rejected
race常用于競爭異步執(zhí)行的結(jié)果場景唤衫,例如:指定時(shí)間內(nèi)沒有獲得結(jié)果就Rejected:
var p = Promise.race([
fetch('/resource'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p.then(response => console.log(response))
.catch(error => console.log(error));
上面代碼中婆赠,如果5秒之內(nèi)無法fetch到數(shù)據(jù),p的狀態(tài)就會變?yōu)镽ejected战授,從而觸發(fā)catch方法指定的回調(diào)函數(shù)页藻。
例子
例如我們要做兩次異步操作(用最簡單的異步操作setTimeout為例)桨嫁,第一次異步操作成功后執(zhí)行第二次異步操作:
function delay(time, callback){
setTimeout(function(){
callback("sleep "+time);
},time);
}
delay(1000,function(msg){
console.log(msg);
delay(2000,function(msg){
console.log(msg);
});
});
//1秒后打印出:sleep 1000
//再過2秒打印出:sleep 2000
上面用回調(diào)嵌套很容易實(shí)現(xiàn),但也發(fā)現(xiàn)才兩層(還沒加上異常處理)份帐,代碼就比較難看了璃吧。改成Promise試試:
function delay(time, callback){
setTimeout(function(){
callback("sleep "+time);
},time);
}
var p = new Promise(function (resolve, reject) {
delay(1000, resolve);
});
p.then(function(value) {
console.log(value);
return new Promise(function (resolve, reject) {
delay(2000, resolve);
});
}).then(function(value) {
console.log(value);
}).catch(console.log.bind(console));
因?yàn)椴艃蓚€(gè)異步操作,所以代碼行數(shù)上看废境,優(yōu)勢不明顯畜挨。優(yōu)勢在于Promise讓代碼的邏輯流程變得更清晰。先執(zhí)行第一次異步操作噩凹,成功后進(jìn)入then巴元,打印出結(jié)果,并執(zhí)行第二次異步操作驮宴,成功后進(jìn)入then逮刨,打印出結(jié)果。
總結(jié)
ES6 Promise的實(shí)現(xiàn)嚴(yán)格遵循了Promise/A+規(guī)范堵泽,用Promise可以寫出更可讀的異步編程代碼修己,避免了回調(diào)地獄。需要注意的是迎罗,由于歷史原因睬愤,有的庫,例如jQuery的Deferred就不是Promise/ A+的規(guī)范纹安,它是個(gè)工廠類尤辱,返回的是內(nèi)部構(gòu)建的deferred對象。本篇篇幅不夠去介紹Deferred和Promise的區(qū)別厢岂,但ES6的Promise完全可以取代Deferred光督。