前端JavaScript之Promise
1、什么是Promise
Promise 是異步編程的一種解決方案。ES6中已經(jīng)提供了原生 Promise 對(duì)象。一個(gè) Promise 對(duì)象會(huì)處于以下幾種狀態(tài)(fulfilled,rejected兩種狀態(tài)一旦確定后不會(huì)改變):
- 待定(
pending): 初始狀態(tài),既沒有被兌現(xiàn),也沒有被拒絕。 - 已兌現(xiàn)(
fulfilled): 意味著操作成功完成。 - 已拒絕(
rejected): 意味著操作失敗。

2、基本用法
Promise 對(duì)象是一個(gè)構(gòu)造函數(shù),用來創(chuàng)建 Promise 實(shí)例,它接收兩個(gè)參數(shù) resolve 和 reject 。
resolve的作用是將Promise對(duì)象的狀態(tài)從pending變?yōu)?fulfilled,在異步操作成功時(shí)調(diào)用,并將異步操作的結(jié)果,作為參數(shù)傳遞出去。reject的作用是將Promise對(duì)象的狀態(tài)從pending變?yōu)?rejected,在異步操作失敗時(shí)調(diào)用,并將異步操作報(bào)出的錯(cuò)誤,作為參數(shù)傳遞出去。
const promise = new Promise(function(resolve, reject) {
// ...
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise 實(shí)例生成以后,使用 then 方法分別指定 fulfilled 狀態(tài)和 rejected 狀態(tài)的回調(diào)函數(shù)。
then接收兩個(gè)參數(shù),第一個(gè)是Promise對(duì)象的狀態(tài)變?yōu)?fulfilled時(shí)的回調(diào)函數(shù),第二個(gè)是狀態(tài)變?yōu)?rejected時(shí)的回調(diào)函數(shù)。
catch接收Promise對(duì)象的狀態(tài)變?yōu)?rejected時(shí)的回調(diào)函數(shù)。
promise.then(function (value){
// ....
},function (err){
// .... err
})
promise.then(function (value){
// ....
}).catch(function (err){
// ....
})
3、Promise的方法
3.1 Promise.prototype.then()
then 方法是定義在原型對(duì)象 Promise.prototype 上,前面說過,它接收兩個(gè)可選參數(shù),第一個(gè)參數(shù)是 fulfilled 狀態(tài)的回調(diào)函數(shù),第二個(gè)參數(shù)是 rejected 狀態(tài)的回調(diào)函數(shù)。
then 方法返回的是一個(gè)新的 Promise 實(shí)例,方便我們采用鏈?zhǔn)綄懛?。比?then 后面接著寫 then ,當(dāng)?shù)谝粋€(gè)回調(diào)函數(shù)完成以后,會(huì)將返回結(jié)果作為參數(shù),傳入第二個(gè)回調(diào)函數(shù)。這種鏈?zhǔn)椒绞娇梢院芊奖愕闹付ㄒ唤M按照次序調(diào)用的回調(diào)函數(shù)。
loadData().then(function (value){
return 3
}).then(function (num){
console.log("ok", num) // 3
})
3.2 Promise.prototype.catch()
catch 方法是用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。如果異步操作拋出錯(cuò)誤,狀態(tài)就會(huì)變?yōu)?rejected ,就會(huì)調(diào)用 catch() 方法指定的回調(diào)函數(shù),處理這個(gè)錯(cuò)誤。
const promise = new Promise(function(resolve, reject) {
throw new Error('unkonw error'); // 拋出錯(cuò)誤狀態(tài)變?yōu)?-> reject
});
const promise = new Promise(function(resolve, reject) {
reject('unkonw error') // 使用reject()方法將狀態(tài)變?yōu)?-> reject
});
promise.catch(function(error) {
console.log(error);
});
Promise 對(duì)象的錯(cuò)誤會(huì)一直向后傳遞,直到被捕獲為止。比如下面代碼: catch 會(huì)捕獲 loadData 和兩個(gè) then 里面拋出的錯(cuò)誤。
loadData().then(function(value) {
return loadData(value);
}).then(function(users) {
}).catch(function(err) {
// 處理前面三個(gè)Promise產(chǎn)生的錯(cuò)誤
});
如果我們不設(shè)置 catch() ,當(dāng)遇到錯(cuò)誤時(shí) Promise 不會(huì)將錯(cuò)誤拋出外面,也就是不會(huì)影響外部代碼執(zhí)行。
const promise = new Promise(function(resolve, reject) {
resolve(a) // ReferenceError: a is not defined
});
promise.then(function(value) {
console.log('value is ', value)
});
setTimeout(() => { console.log('code is run') }, 1000); // code is run
3.3 Promise.prototype.finally()
finally() 方法不管 Promise 對(duì)象最后狀態(tài)如何,都會(huì)執(zhí)行的操作。下面是我們使用 Promise 的一個(gè)常規(guī)結(jié)構(gòu)。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
3.4 Promise.all()
Promise.all() 方法可以將多個(gè) Promise 實(shí)例包裝成一個(gè)新的 Promise 實(shí)例返回。
const promise = Promise.all([p1, p2, p3]);
新 promise 狀態(tài)來依賴于“傳入的 promise ”。
- 只有當(dāng)所有“傳入的
promise”狀態(tài)都變成fulfilled,它的狀態(tài)才會(huì)變成fulfilled,此時(shí)“傳入的promise”返回值組成一個(gè)數(shù)組,傳遞給promise的回調(diào)函數(shù)。 - 如果“傳入的 promise ”之中有一個(gè)被 rejected ,新
promise的狀態(tài)就會(huì)變成rejected,此時(shí)第一個(gè)被reject的promise的返回值,會(huì)傳遞給promise的回調(diào)函數(shù)。
const promises = [1,2,3,4].map(function (id) {
return loadData(id);
});
Promise.all(promises).then(function (users) {
// ...
}).catch(function(err){
// ...
});
3.5 Promise.race()
Promise.race() 方法同樣是將多個(gè) Promise 實(shí)例,包裝成一個(gè)新的 Promise 實(shí)例。
Promise.race() 方法的參數(shù)與 Promise.all() 方法一樣。
const promise = Promise.race([p1, p2, p3]);
Promise.all() 和 Promise.race() 對(duì)比:
Promise.all() ,如果所有都執(zhí)行成功則返回所有成功的 promise 值,如果有失敗則返回第一個(gè)失敗的值。
Promise.race() ,返回第一個(gè)執(zhí)行完成的 promise 值,它可能是fulfilled和rejected狀態(tài)。
這兩個(gè)方法的使用場景。
場景一:用戶登錄社交網(wǎng)站主頁后,會(huì)同時(shí)異步請(qǐng)求拉取用戶信息,關(guān)注列表,粉絲列表,我們需要保證所有數(shù)據(jù)請(qǐng)求成功再進(jìn)行渲染頁面,只要有一個(gè)數(shù)據(jù)不成功就會(huì)重定向頁面,這里可以使用 Promise.all 。
function initUserHome() {
Promise.all([
new Promise(getMe),
new Promise(getFollows),
new Promise(getFans)
])
.then(function(data){
// 顯示頁面
})
.catch(function(err){
// .... 重定向頁面
});
};
initUserHome();
場景二:假如我們?cè)谧鲆粋€(gè)搶票軟件,雖然請(qǐng)求了很多賣票渠道,每次只需返回第一個(gè)執(zhí)行完成的 Promise ,這里可以使用 Promise.race 。
function getTicket() {
Promise.race([
new Promise(postASell),
new Promise(postBSell),
new Promise(postCSell)
])
.then(function(data){
// 搶票成功
})
.catch(function(err){
// .... 搶票失敗,重試
});
};
getTicket();
3.6 Promise.allSettled()
使用 Promise.all() 時(shí),如果有一個(gè) Promise 失敗后,其它 Promise 不會(huì)停止執(zhí)行。
const requests = [
fetch('/url1'),
fetch('/url2'),
fetch('/url3'),
];
try {
await Promise.all(requests);
console.log('所有請(qǐng)求都成功。');
} catch {
console.log('有一個(gè)請(qǐng)求失敗,其他請(qǐng)求可能還沒結(jié)束。');
}
有的時(shí)候,我們希望等到一組異步操作都結(jié)束了,再進(jìn)行下一步操作。這時(shí)就需要使用 Promise.allSettled() ,的它參數(shù)是一個(gè)數(shù)組,數(shù)組的每個(gè)成員都是一個(gè) Promise 對(duì)象,返回一個(gè)新的 Promise 對(duì)象。它只有等到參數(shù)數(shù)組的所有 Promise 對(duì)象都發(fā)生狀態(tài)變更(不管是 fulfilled 還是 rejected ),返回的 Promise 對(duì)象才會(huì)發(fā)生狀態(tài)變更。
const requests = [
fetch('/url1'),
fetch('/url2'),
fetch('/url3'),
];
await Promise.allSettled(requests);
console.log('所有請(qǐng)求完成后(包括成功失?。﹫?zhí)行');
3.7 Promise.any()
只要傳入的 Promise 有一個(gè)變成 fulfilled 狀態(tài),新的 Promise 就會(huì)變成 fulfilled 狀態(tài);如果所有傳入的 Promise 都變成 rejected 狀態(tài),新的 Promise 就會(huì)變成 rejected 狀態(tài)。
Promise.any() 和 Promise.race() 差不多,區(qū)別在于 Promise.any() 不會(huì)因?yàn)槟硞€(gè) Promise 變成 rejected 狀態(tài)而結(jié)束,必須等到所有參數(shù) Promise 變成 rejected 狀態(tài)才會(huì)結(jié)束。
回到 Promise.race() 多渠道搶票的場景,如果我們需要保證要么有一個(gè)渠道搶票成功,要么全部渠道都失敗,使用 Promise.any() 就顯得更合適。
function getTicket() {
Promise.any([
new Promise(postASell),
new Promise(postBSell),
new Promise(postCSell)
])
.then(function(data){
// 有一個(gè)搶票成功
})
.catch(function(err){
// .... 所有渠道都失敗了
});
};
getTicket();
3.8 Promise.resolve()
Promise.resolve() 方法將現(xiàn)有對(duì)象轉(zhuǎn)換為 Promise 對(duì)象。等價(jià)于下面代碼:
new Promise(resolve => resolve(1))
傳入的參數(shù)不同,處理
- 參數(shù)
Promise實(shí)例,它將不做任何修改、原封不動(dòng)地返回這個(gè)實(shí)例。 - 參數(shù)
thenable對(duì)象,它會(huì)將這個(gè)對(duì)象轉(zhuǎn)為Promise對(duì)象,然后就立即執(zhí)行thenable對(duì)象的then()方法。
let thenable = {
then: function(resolve, reject) {
resolve(1);
}
};
- 參數(shù)是普通值,返回一個(gè)新的
Promise對(duì)象,狀態(tài)為resolved。
const promise = Promise.resolve(1);
promise.then(function (value) {
console.log(value) // 1
});
- 無參數(shù),直接返回一個(gè)
resolved狀態(tài)的Promise對(duì)象。
3.9 Promise.reject()
Promise.reject(reason) 方法也會(huì)返回一個(gè)新的 Promise 實(shí)例,該實(shí)例的狀態(tài)為 rejected 。
const promise = Promise.reject('unkonw error');
// 相當(dāng)于
const promise = new Promise((resolve, reject) => reject('unkonw error'))
promise.then(null, function (s) {
console.log(s)
});
// unkonw error
4、簡單場景
異步加載圖片:
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = url;
});
}
請(qǐng)求超時(shí)處理:
//請(qǐng)求
function request(){
return new Promise(function(resolve, reject){
// code ....
resolve('request ok')
})
}
function timeoutHandle(time){
return new Promise(function(resolve, reject){
setTimeout(function(){
reject('timeout');
}, time);
});
}
Promise.race([
request(),
timeoutHandle(5000)
])
.then(res=>{
console.log(res)
}).catch(err=>{
console.log(err)// timeout
})
到此這篇關(guān)于前端JavaScript之Promise的文章就介紹到這了,更多相關(guān)JavaScript Promise內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解析JS參數(shù)parseInt('012',?16)和parseInt(012,?16)是否相等
這篇文章主要為大家介紹了parseInt('012',?16)和parseInt(012,?16)是否相等原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
微信小程序之MaterialDesign--input組件詳解
本篇文章主要介紹了微信小程序之MaterialDesign--input組件詳解,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-02-02
JS時(shí)間分片技術(shù)解決長任務(wù)導(dǎo)致的頁面卡頓
旨在把一個(gè)運(yùn)行時(shí)間比較長的任務(wù)分解成一塊一塊比較小的任務(wù),分塊去執(zhí)行,因?yàn)槌^ 50ms 的任務(wù)就會(huì)被認(rèn)為是 long task,用戶就能感知到渲染卡頓和交互的卡頓,所以我們可以縮短函數(shù)的連續(xù)執(zhí)行時(shí)間2022-07-07

