1、Promise
在Promise
以前擂找,在需要多個(gè)異步操作的時(shí)候,會(huì)導(dǎo)致多個(gè)回調(diào)函數(shù)嵌套浩销,導(dǎo)致代碼不夠直觀贯涎,就是常說的回調(diào)地獄。
aFunc(function(aRes) {
bFunc(aRes, function(bRes) {
cFunc(bRes, function(cRes) {
console.log('得到最終結(jié)果: ' + cRes);
}, callback);
}, callback);
}, callback);
通常通過 Promise
來解決慢洋,Promise
本意是承諾塘雳,在程序中的意思就是承諾我過一段時(shí)間后會(huì)給你一個(gè)結(jié)果陆盘。 什么時(shí)候會(huì)用到過一段時(shí)間?答案是異步操作败明,異步是指可能比較長時(shí)間才有結(jié)果的才做隘马,例如網(wǎng)絡(luò)請(qǐng)求、讀取本地文件等妻顶。
aFunc().then(function(aRes) {
return bFunc(aRes);
})
.then(function(bRes) {
return cFunc(bRes);
})
.then(function(cRes) {
console.log('得到最終結(jié)果: ' + cRes);
})
.catch(callback);
1.1酸员、Promise狀態(tài)
Promise
必須為以下三種狀態(tài)之一:等待態(tài)(Pending)、執(zhí)行態(tài)(Fulfilled)和拒絕態(tài)(Rejected)讳嘱。一旦Promise
被 resolve 或 reject幔嗦,不能再遷移至其他任何狀態(tài)(即狀態(tài) immutable)。
1.2沥潭、Promise過程
- 初始化
Promise
狀態(tài)(pending) - 立即執(zhí)行
Promise
中傳入的 fn 函數(shù)邀泉,將Promise
內(nèi)部 resolve、reject 函數(shù)作為參數(shù)傳遞給 fn 钝鸽,按事件機(jī)制時(shí)機(jī)處理 - 執(zhí)行 then(..) 注冊回調(diào)處理數(shù)組(then 方法可被同一個(gè) promise 調(diào)用多次)
-
Promise
里的關(guān)鍵是要保證汇恤,then方法傳入的參數(shù) onFulfilled 和 onRejected,必須在then方法被調(diào)用的那一輪事件循環(huán)之后的新執(zhí)行棧中執(zhí)行拔恰。
2屁置、Promise用法
Promise
對(duì)象是一個(gè)構(gòu)造函數(shù),用來生成Promise
實(shí)例仁连。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise
構(gòu)造函數(shù)接受一個(gè)函數(shù)作為參數(shù)蓝角,該函數(shù)的兩個(gè)參數(shù)分別是resolve
和reject
-
resolve
函數(shù)的作用是,將Promise
對(duì)象的狀態(tài)從“未完成”變?yōu)椤俺晒Α?/li> -
reject
函數(shù)的作用是饭冬,將Promise
對(duì)象的狀態(tài)從“未完成”變?yōu)椤笆 ?/li>
3使鹅、Promise對(duì)象之實(shí)例方法
3.1、Promise.prototype.then()
Promise
實(shí)例具有 then
方法昌抠,也就是說患朱,then
方法是定義在原型對(duì)象Promise.prototype
上的。它的作用是為 Promise
實(shí)例添加狀態(tài)改變時(shí)的回調(diào)函數(shù)炊苫。前面說過裁厅, then
方法的第一個(gè)參數(shù)是 resolved
狀態(tài)對(duì)應(yīng)的回調(diào)函數(shù), 第二個(gè)參數(shù)(可選) 是 rejected
狀態(tài)對(duì)應(yīng)的回調(diào)函數(shù)。
3.2侨艾、Promise.prototype.catch()
Promise.prototype.catch()
方法是 .then(null,rejection)
或 .then(undefined,rejection)
的別名执虹,用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。
getJSON('/posts.json').then(function(posts) {
// ...
}).catch(function(error) {
// 處理 getJSON 和 前一個(gè)回調(diào)函數(shù)運(yùn)行時(shí)發(fā)生的錯(cuò)誤
console.log('發(fā)生錯(cuò)誤唠梨!', error);
});
上面代碼中袋励,getJSON()
方法返回一個(gè) Promise 對(duì)象,如果該對(duì)象狀態(tài)變?yōu)?code>resolved,則會(huì)調(diào)用then()
方法指定的回調(diào)函數(shù)茬故;如果異步操作拋出錯(cuò)誤盖灸,狀態(tài)就會(huì)變?yōu)?code>rejected,就會(huì)調(diào)用catch()
方法指定的回調(diào)函數(shù)磺芭,處理這個(gè)錯(cuò)誤赁炎。另外,then()
方法指定的回調(diào)函數(shù)钾腺,如果運(yùn)行中拋出錯(cuò)誤徙垫,也會(huì)被catch()
方法捕獲。
3.3垮庐、Promise.prototype.finally()
finally()
方法用于指定不管 Promise 對(duì)象最后狀態(tài)如何松邪,都會(huì)執(zhí)行的操作坞琴。
getJSON('/posts.json').
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
上面代碼中哨查,不管promise
最后的狀態(tài),在執(zhí)行完then
或catch
指定的回調(diào)函數(shù)以后剧辐,都會(huì)執(zhí)行finally
方法指定的回調(diào)函數(shù)寒亥。
3.4、Promise.all()
Promise.all()
方法用于將多個(gè) Promise 實(shí)例荧关,包裝成一個(gè)新的 Promise 實(shí)例溉奕。
const p = Promise.all([p1, p2, p3]);
上面代碼中,Promise.all()
方法接受一個(gè)數(shù)組作為參數(shù)忍啤,p1
加勤、p2
、p3
都是 Promise 實(shí)例同波,如果不是鳄梅,就會(huì)先調(diào)用下面講到的Promise.resolve
方法,將參數(shù)轉(zhuǎn)為 Promise 實(shí)例未檩,再進(jìn)一步處理戴尸。另外,Promise.all()
方法的參數(shù)可以不是數(shù)組冤狡,但必須具有 Iterator 接口孙蒙,且返回的每個(gè)成員都是 Promise 實(shí)例。
p
的狀態(tài)由p1
悲雳、p2
挎峦、p3
決定,分成兩種情況合瓢。
(1)只有p1
浑测、p2
、p3
的狀態(tài)都變成fulfilled
,p
的狀態(tài)才會(huì)變成fulfilled
迁央,此時(shí)p1
掷匠、p2
、p3
的返回值組成一個(gè)數(shù)組岖圈,傳遞給p
的回調(diào)函數(shù)讹语。
(2)只要p1
、p2
蜂科、p3
之中有一個(gè)被rejected
顽决,p
的狀態(tài)就變成rejected
,此時(shí)第一個(gè)被reject
的實(shí)例的返回值导匣,會(huì)傳遞給p
的回調(diào)函數(shù)才菠。
3.5、Promise.race()
Promise.race()
方法同樣是將多個(gè) Promise 實(shí)例贡定,包裝成一個(gè)新的 Promise 實(shí)例赋访。
const p = Promise.all([p1, p2, p3]);
上面代碼中,只要p1
缓待、p2
蚓耽、p3
之中有一個(gè)實(shí)例率先改變狀態(tài),p
的狀態(tài)就跟著改變旋炒。那個(gè)率先改變的 Promise 實(shí)例的返回值步悠,就傳遞給p
的回調(diào)函數(shù)。
4瘫镇、Promise使用場景
- 將圖片的加載寫成一個(gè)
Promise
鼎兽,一旦加載完成,Promise
的狀態(tài)就發(fā)生變化
const preloadImage = function (path) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = path;
});
};
- 通過鏈?zhǔn)讲僮飨吵瑢⒍鄠€(gè)渲染數(shù)據(jù)分別給個(gè)
then
谚咬,讓其各自處理數(shù)據(jù)⊥酰或當(dāng)下個(gè)異步請(qǐng)求依賴上個(gè)請(qǐng)求結(jié)果的時(shí)候序宦,我們也能夠通過鏈?zhǔn)讲僮饔押媒鉀Q問題
getData().then(res=>{
let { bannerList } = res
console.log(bannerList)
return res
}).then(res=>{
let { storeList } = res
console.log(storeList)
return res
}).then(res=>{
let { categoryList } = res
console.log(categoryList)
return res
})
- 通過
all()
實(shí)現(xiàn)多個(gè)請(qǐng)求合并在一起,匯總所有請(qǐng)求結(jié)果
function initLoad(){
// loading.show()
Promise.all([getBannerList(),getStoreList(),getCategoryList()]).then(res=>{
console.log(res)
loading.hide()
}).catch(err=>{
console.log(err)
loading.hide()
})
}
//數(shù)據(jù)初始化
initLoad()
- 通過
race
可以設(shè)置圖片請(qǐng)求超時(shí)
function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
//img.src = "xx.jpg"; 正確的
img.src = "xxx.jpggg"; // 錯(cuò)誤的
});
return p;
}
//延時(shí)函數(shù)背苦,用于給請(qǐng)求計(jì)時(shí)
function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
reject('圖片請(qǐng)求超時(shí)');
}, 5000);
});
return p;
}
Promise
.race([requestImg(), timeout()])
.then(function(results){
console.log(results);
})
.catch(function(reason){
console.log(reason);
});