一.關(guān)于Promise
promise 是異步編程的一種解決方案杯矩,比傳統(tǒng)的解決方案(回調(diào)函數(shù)和事件)更合理和更強大沽损。它由社區(qū)最早提出和實現(xiàn)亮元,ES6將其寫進了語言標準猛计,統(tǒng)一了用法,原生提供了Promise
對象爆捞。
所謂Promise
奉瘤,簡單說就是一個容器,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果。從語法上說盗温,Promise 是一個對象藕赞,從它可以獲取異步操作的消息。Promise 提供統(tǒng)一的 API卖局,各種異步操作都可以用同樣的方法進行處理斧蜕。
它代表一個異步操作。有三種狀態(tài):
pending
(進行中): 初始狀態(tài), 初始狀態(tài)砚偶,未完成或拒絕批销。
resolved
(已完成): 意味著操作成功完成。又名fulfilled染坯。
rejected
(已失敗): 意味著操作失敗风钻。
有了Promise
,就可以將異步操作以同步操作的流程表達出酒请,但它也有如下缺點:
一旦創(chuàng)建它就會立即執(zhí)行骡技,無法中途取消。
如果不設(shè)置回調(diào)函數(shù)羞反,Promise
內(nèi)部拋出的錯誤布朦,不會反應(yīng)到外部。
當處于Pending
狀態(tài)時昼窗,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)是趴。
二.應(yīng)用示例
- 創(chuàng)建Promise
var promise = new Promise(
/* executor */
function(resolve, reject){
...
}
);
? executor函數(shù)由Promise實現(xiàn)立即執(zhí)行,傳遞resolve和reject函數(shù). (在Promise構(gòu)造函數(shù)之前調(diào)用執(zhí)行器甚至返回創(chuàng)建的對象)
? 在 executor內(nèi)部澄惊,promise有如下的狀態(tài)變化可能:
? 1. resolve被調(diào)用唆途,promise由pending變?yōu)閞esolved,代表該Promise被成功解析(resolve)
? 2.reject被調(diào)用掸驱,promise由pending變?yōu)閞ejected肛搬,代表該Promise的值不能用于后續(xù)處理,即被拒絕了
注意:
如果在executor方法的執(zhí)行過程中拋出了任何異常毕贼,那么promise立即被拒絕(即相當于reject方法被調(diào)用)温赔,executor的返回值也就會被忽略。 如果一個promise對象處在resolved或rejected狀態(tài)而不是pending狀態(tài)鬼癣,那么它也可以被稱為settled狀態(tài)陶贼。
2.處理Promise實例
then:Promise實例生成以后,可以用then方法分別指定Resolved狀態(tài)和Reject狀態(tài)的回調(diào)函數(shù)待秃。
promise.then(function(value) {
// success 狀態(tài)為Resolved時調(diào)用
}, function(error) {
// failure 狀態(tài)為Reject時調(diào)用
});
then方法可以接受兩個回調(diào)函數(shù)作為參數(shù)拜秧。
第一個回調(diào)函數(shù):在promise的狀態(tài)變成resolved時被調(diào)用。它的參數(shù)promise的resolve方法的返回值章郁。
第二個回調(diào)函數(shù):在promise的狀態(tài)變成rejected時被調(diào)用枉氮。它的參數(shù)promise的reject方法的返回值。且為可選參數(shù)。
catch:Promise.prototype.catch方法是.then(null, rejection)的別名嘲恍,用于指定發(fā)生錯誤時的回調(diào)函數(shù)。
promise.then(function(posts) {
// ...
}).catch(function(error) {
console.log('發(fā)生錯誤雄驹!', error); // 處理 promise 和 前一個回調(diào)函數(shù)運行時發(fā)生的錯誤
});
擴展:
Promise.prototype.then
和 Promise.prototype.catch
方法返回 promises對象佃牛, 所以它們可以被鏈式調(diào)用。但此時返回的是以函數(shù)返回值生成的新的Promise實例医舆,不是原來那個Promise實例俘侠。[圖片上傳中。蔬将。爷速。(1)]
注意問題:
Promise 對象的錯誤具有“冒泡”性質(zhì),會一直向后傳遞霞怀,直到被捕獲為止惫东。也就是說,錯誤總是會被下一個catch語句捕獲毙石。
3.將多個Promise實例廉沮,包裝成一個新的Promise實例
**Promise.all(iterable)
** :當所有在可迭代參數(shù)中的 promises 已完成,或者第一個傳遞的 promise(指 reject)失敗時徐矩,返回 promise滞时。
var p = Promise.all([p1, p2, p3]);
當p1、p2和p3的狀態(tài)均為resolved時p的狀態(tài)為resolved滤灯。
當p1坪稽、p2或p3中的任意一個被rejected,p的狀態(tài)就變成rejected鳞骤,此時第一個被reject的實例的返回值窒百,會傳遞給p的回調(diào)函數(shù)。
具體的使用示例如下:
情形一:全部成功的情況
Promise.all([Promise.resolve('foo'),Promise.resolve('bar'),Promise.resolve('test')])
.then(function(result){
console.log(result); //結(jié)果為:[foo,bar,test]豫尽。即所有返回值的數(shù)組贝咙。
})
.catch(function(error) {
console.log('error'); //不會被執(zhí)行
});
情形二:全部失敗或部分失敗
Promise.all([Promise.resolve('foo'),Promise.reject('barError'),Promise.reject('testError')])
.then(function(result){
console.log(result); //成功回調(diào) 不會被執(zhí)行
})
.catch(function(error) {
console.log(error); //結(jié)果為barError或testError。即第一個被reject的值拂募。
});
三.常見的使用誤區(qū)
忘記添加 .catch()
通常情況庭猩,promise有如下兩種處理方式:
// ------------ 不好的寫法 -------------
promise.then(function(data) {
// success
}, function(err) { //僅處理promise運行時發(fā)生的錯誤。無法處理回調(diào)中的錯誤
// error
});
// ------------ 好的寫法 ------------
promise.then(function(data) {
// success
}).catch(function(err) { // 處理 promise 和 前一個回調(diào)函數(shù)運行時發(fā)生的錯誤
// error
});
因為promise拋出的錯誤不會傳遞到外層代碼陈症。當使用沒有catch的第一種種寫法時蔼水,成功回調(diào)的錯誤將無法被處理。因此比較好的方式是录肯,總是使用catch方法趴腋。
在then或者catch函數(shù)中不使用return下面是在是用Promise時,一個很常見的錯誤寫法:
//------------ 不好的寫法 ------------------
promise.then(function () {
getUserInfo(userId);
}).then(function () {
// 在這里可能希望在這個回調(diào)中使用用戶信息,但你可能會發(fā)現(xiàn)它根本不存在
});
會發(fā)生上面的錯誤优炬,是因為對Promise的返回及鏈式調(diào)用的理解不夠颁井。
每一個promise都會給你一個then()方法(或者catch,它們只是then(null,…)的語法糖)蠢护。這里我們是在then()方法的內(nèi)部來看:
promise.then(function () {
return getUserInfo(userId);
}).then(function (userInfo) {
HadleUser(userInfo);
}).catch(function(error) {
console.log(error);
});
在回調(diào)中在有三種事可以做:
返回另一個promise
如果getUserInfo返回一個Promise雅宾,則HadleUser將在該promise變?yōu)閞esolved后執(zhí)行,并以該promise的返回作為入?yún)ⅰ?br>
返回一個同步值(或者undefined)
getUserInfo返回一個同步值葵硕,則改值會被包裝成一個resolved狀態(tài)的promise眉抬,HadleUser
將被立刻執(zhí)行,并以getUserInfo返回作為入?yún)ⅰ?br>
拋出一個同步錯誤懈凹。
getUserInfo返回一個同步錯誤蜀变,則該錯誤會被包裝成一個rejected狀態(tài)的promise,最終在catch中被捕獲介评。此時HadleUser將不會被執(zhí)行库北。
promises丟失
你認為下面的代碼會打印出什么?
Promise.resolve('foo').then(Promise.resolve('bar')).then(function (result) {
console.log(result);
});
如果你認為打印出bar们陆,那你就大錯特錯了贤惯。它實際上會打印出foo。
原因是當你給then()傳遞一個非函數(shù)(比如一個promise)值的時候棒掠,它實際上會解釋為then(null)孵构,這會導(dǎo)致之前的promise的結(jié)果丟失。
四.最佳實踐總結(jié)
- then方法中 永遠 return或 throw
- 如果 promise鏈中可能出現(xiàn)錯誤烟很,一定添加 catch
- 永遠傳遞函數(shù)給 then方法
- 不要把 promise寫成嵌套
參考鏈接:
Promise -JavaScript | MDN
Promises 很酷颈墅,但很多人并沒有理解就在用了
Promise 對象