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

2、基本用法
Promise 對象是一個構(gòu)造函數(shù),用來創(chuàng)建 Promise 實例,它接收兩個參數(shù) resolve 和 reject 。
resolve的作用是將Promise對象的狀態(tài)從pending變?yōu)?fulfilled,在異步操作成功時調(diào)用,并將異步操作的結(jié)果,作為參數(shù)傳遞出去。reject的作用是將Promise對象的狀態(tài)從pending變?yōu)?rejected,在異步操作失敗時調(diào)用,并將異步操作報出的錯誤,作為參數(shù)傳遞出去。
const promise = new Promise(function(resolve, reject) {
// ...
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise 實例生成以后,使用 then 方法分別指定 fulfilled 狀態(tài)和 rejected 狀態(tài)的回調(diào)函數(shù)。
then接收兩個參數(shù),第一個是Promise對象的狀態(tài)變?yōu)?fulfilled時的回調(diào)函數(shù),第二個是狀態(tài)變?yōu)?rejected時的回調(diào)函數(shù)。
catch接收Promise對象的狀態(tài)變?yōu)?rejected時的回調(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 方法是定義在原型對象 Promise.prototype 上,前面說過,它接收兩個可選參數(shù),第一個參數(shù)是 fulfilled 狀態(tài)的回調(diào)函數(shù),第二個參數(shù)是 rejected 狀態(tài)的回調(diào)函數(shù)。
then 方法返回的是一個新的 Promise 實例,方便我們采用鏈式寫法。比如 then 后面接著寫 then ,當?shù)谝粋€回調(diào)函數(shù)完成以后,會將返回結(jié)果作為參數(shù),傳入第二個回調(diào)函數(shù)。這種鏈式方式可以很方便的指定一組按照次序調(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ā)生錯誤時的回調(diào)函數(shù)。如果異步操作拋出錯誤,狀態(tài)就會變?yōu)?rejected ,就會調(diào)用 catch() 方法指定的回調(diào)函數(shù),處理這個錯誤。
const promise = new Promise(function(resolve, reject) {
throw new Error('unkonw error'); // 拋出錯誤狀態(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 對象的錯誤會一直向后傳遞,直到被捕獲為止。比如下面代碼: catch 會捕獲 loadData 和兩個 then 里面拋出的錯誤。
loadData().then(function(value) {
return loadData(value);
}).then(function(users) {
}).catch(function(err) {
// 處理前面三個Promise產(chǎn)生的錯誤
});
如果我們不設置 catch() ,當遇到錯誤時 Promise 不會將錯誤拋出外面,也就是不會影響外部代碼執(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 對象最后狀態(tài)如何,都會執(zhí)行的操作。下面是我們使用 Promise 的一個常規(guī)結(jié)構(gòu)。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
3.4 Promise.all()
Promise.all() 方法可以將多個 Promise 實例包裝成一個新的 Promise 實例返回。
const promise = Promise.all([p1, p2, p3]);
新 promise 狀態(tài)來依賴于“傳入的 promise ”。
- 只有當所有“傳入的
promise”狀態(tài)都變成fulfilled,它的狀態(tài)才會變成fulfilled,此時“傳入的promise”返回值組成一個數(shù)組,傳遞給promise的回調(diào)函數(shù)。 - 如果“傳入的 promise ”之中有一個被 rejected ,新
promise的狀態(tài)就會變成rejected,此時第一個被reject的promise的返回值,會傳遞給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() 方法同樣是將多個 Promise 實例,包裝成一個新的 Promise 實例。
Promise.race() 方法的參數(shù)與 Promise.all() 方法一樣。
const promise = Promise.race([p1, p2, p3]);
Promise.all() 和 Promise.race() 對比:
Promise.all() ,如果所有都執(zhí)行成功則返回所有成功的 promise 值,如果有失敗則返回第一個失敗的值。
Promise.race() ,返回第一個執(zhí)行完成的 promise 值,它可能是fulfilled和rejected狀態(tài)。
這兩個方法的使用場景。
場景一:用戶登錄社交網(wǎng)站主頁后,會同時異步請求拉取用戶信息,關(guān)注列表,粉絲列表,我們需要保證所有數(shù)據(jù)請求成功再進行渲染頁面,只要有一個數(shù)據(jù)不成功就會重定向頁面,這里可以使用 Promise.all 。
function initUserHome() {
Promise.all([
new Promise(getMe),
new Promise(getFollows),
new Promise(getFans)
])
.then(function(data){
// 顯示頁面
})
.catch(function(err){
// .... 重定向頁面
});
};
initUserHome();
場景二:假如我們在做一個搶票軟件,雖然請求了很多賣票渠道,每次只需返回第一個執(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() 時,如果有一個 Promise 失敗后,其它 Promise 不會停止執(zhí)行。
const requests = [
fetch('/url1'),
fetch('/url2'),
fetch('/url3'),
];
try {
await Promise.all(requests);
console.log('所有請求都成功。');
} catch {
console.log('有一個請求失敗,其他請求可能還沒結(jié)束。');
}
有的時候,我們希望等到一組異步操作都結(jié)束了,再進行下一步操作。這時就需要使用 Promise.allSettled() ,的它參數(shù)是一個數(shù)組,數(shù)組的每個成員都是一個 Promise 對象,返回一個新的 Promise 對象。它只有等到參數(shù)數(shù)組的所有 Promise 對象都發(fā)生狀態(tài)變更(不管是 fulfilled 還是 rejected ),返回的 Promise 對象才會發(fā)生狀態(tài)變更。
const requests = [
fetch('/url1'),
fetch('/url2'),
fetch('/url3'),
];
await Promise.allSettled(requests);
console.log('所有請求完成后(包括成功失?。﹫?zhí)行');
3.7 Promise.any()
只要傳入的 Promise 有一個變成 fulfilled 狀態(tài),新的 Promise 就會變成 fulfilled 狀態(tài);如果所有傳入的 Promise 都變成 rejected 狀態(tài),新的 Promise 就會變成 rejected 狀態(tài)。
Promise.any() 和 Promise.race() 差不多,區(qū)別在于 Promise.any() 不會因為某個 Promise 變成 rejected 狀態(tài)而結(jié)束,必須等到所有參數(shù) Promise 變成 rejected 狀態(tài)才會結(jié)束。
回到 Promise.race() 多渠道搶票的場景,如果我們需要保證要么有一個渠道搶票成功,要么全部渠道都失敗,使用 Promise.any() 就顯得更合適。
function getTicket() {
Promise.any([
new Promise(postASell),
new Promise(postBSell),
new Promise(postCSell)
])
.then(function(data){
// 有一個搶票成功
})
.catch(function(err){
// .... 所有渠道都失敗了
});
};
getTicket();
3.8 Promise.resolve()
Promise.resolve() 方法將現(xiàn)有對象轉(zhuǎn)換為 Promise 對象。等價于下面代碼:
new Promise(resolve => resolve(1))
傳入的參數(shù)不同,處理
- 參數(shù)
Promise實例,它將不做任何修改、原封不動地返回這個實例。 - 參數(shù)
thenable對象,它會將這個對象轉(zhuǎn)為Promise對象,然后就立即執(zhí)行thenable對象的then()方法。
let thenable = {
then: function(resolve, reject) {
resolve(1);
}
};
- 參數(shù)是普通值,返回一個新的
Promise對象,狀態(tài)為resolved。
const promise = Promise.resolve(1);
promise.then(function (value) {
console.log(value) // 1
});
- 無參數(shù),直接返回一個
resolved狀態(tài)的Promise對象。
3.9 Promise.reject()
Promise.reject(reason) 方法也會返回一個新的 Promise 實例,該實例的狀態(tài)為 rejected 。
const promise = Promise.reject('unkonw error');
// 相當于
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;
});
}
請求超時處理:
//請求
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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript數(shù)據(jù)類型之原始類型詳解
這篇文章主要為大家介紹了javascript數(shù)據(jù)類型之原始類型詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06
JavaScript?Page?Visibility?API檢測頁面是否可見方法詳解
這篇文章主要為大家介紹了JavaScript?Page?Visibility?API檢測頁面是否可見方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05

