前端JavaScript之Promise
1、什么是Promise
Promise
是異步編程的一種解決方案。ES6中已經(jīng)提供了原生 Promise
對象。一個(gè) Promise
對象會處于以下幾種狀態(tài)(fulfilled
,rejected
兩種狀態(tài)一旦確定后不會改變):
- 待定(
pending
): 初始狀態(tài),既沒有被兌現(xiàn),也沒有被拒絕。 - 已兌現(xiàn)(
fulfilled
): 意味著操作成功完成。 - 已拒絕(
rejected
): 意味著操作失敗。
2、基本用法
Promise
對象是一個(gè)構(gòu)造函數(shù),用來創(chuàng)建 Promise
實(shí)例,它接收兩個(gè)參數(shù) resolve
和 reject
。
resolve
的作用是將Promise
對象的狀態(tài)從pending
變?yōu)?fulfilled
,在異步操作成功時(shí)調(diào)用,并將異步操作的結(jié)果,作為參數(shù)傳遞出去。reject
的作用是將Promise
對象的狀態(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
對象的狀態(tài)變?yōu)?fulfilled
時(shí)的回調(diào)函數(shù),第二個(gè)是狀態(tài)變?yōu)?rejected
時(shí)的回調(diào)函數(shù)。
catch
接收Promise
對象的狀態(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
方法是定義在原型對象 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ù)完成以后,會將返回結(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)就會變?yōu)?rejected
,就會調(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
對象的錯(cuò)誤會一直向后傳遞,直到被捕獲為止。比如下面代碼: catch
會捕獲 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
不會將錯(cuò)誤拋出外面,也就是不會影響外部代碼執(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 的一個(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)才會變成fulfilled
,此時(shí)“傳入的promise
”返回值組成一個(gè)數(shù)組,傳遞給promise
的回調(diào)函數(shù)。 - 如果“傳入的 promise ”之中有一個(gè)被 rejected ,新
promise
的狀態(tài)就會變成rejected
,此時(shí)第一個(gè)被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()
方法同樣是將多個(gè) Promise
實(shí)例,包裝成一個(gè)新的 Promise
實(shí)例。
Promise.race()
方法的參數(shù)與 Promise.all()
方法一樣。
const promise = Promise.race([p1, p2, p3]);
Promise.all()
和 Promise.race
()
對比:
Promise.all()
,如果所有都執(zhí)行成功則返回所有成功的 promise
值,如果有失敗則返回第一個(gè)失敗的值。
Promise.race()
,返回第一個(gè)執(zhí)行完成的 promise
值,它可能是fulfilled
和rejected
狀態(tài)。
這兩個(gè)方法的使用場景。
場景一:用戶登錄社交網(wǎng)站主頁后,會同時(shí)異步請求拉取用戶信息,關(guān)注列表,粉絲列表,我們需要保證所有數(shù)據(jù)請求成功再進(jìn)行渲染頁面,只要有一個(gè)數(shù)據(jù)不成功就會重定向頁面,這里可以使用 Promise.all
。
function initUserHome() { Promise.all([ new Promise(getMe), new Promise(getFollows), new Promise(getFans) ]) .then(function(data){ // 顯示頁面 }) .catch(function(err){ // .... 重定向頁面 }); }; initUserHome();
場景二:假如我們在做一個(gè)搶票軟件,雖然請求了很多賣票渠道,每次只需返回第一個(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 不會停止執(zhí)行。
const requests = [ fetch('/url1'), fetch('/url2'), fetch('/url3'), ]; try { await Promise.all(requests); console.log('所有請求都成功。'); } catch { console.log('有一個(gè)請求失敗,其他請求可能還沒結(jié)束。'); }
有的時(shí)候,我們希望等到一組異步操作都結(jié)束了,再進(jìn)行下一步操作。這時(shí)就需要使用 Promise.allSettled()
,的它參數(shù)是一個(gè)數(shù)組,數(shù)組的每個(gè)成員都是一個(gè) Promise
對象,返回一個(gè)新的 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
有一個(gè)變成 fulfilled
狀態(tài),新的 Promise
就會變成 fulfilled
狀態(tài);如果所有傳入的 Promise
都變成 rejected
狀態(tài),新的 Promise 就會變成 rejected 狀態(tài)。
Promise.any()
和 Promise.race()
差不多,區(qū)別在于 Promise.any()
不會因?yàn)槟硞€(gè) Promise
變成 rejected
狀態(tài)而結(jié)束,必須等到所有參數(shù) Promise
變成 rejected
狀態(tài)才會結(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)有對象轉(zhuǎn)換為 Promise
對象。等價(jià)于下面代碼:
new Promise(resolve => resolve(1))
傳入的參數(shù)不同,處理
- 參數(shù)
Promise
實(shí)例,它將不做任何修改、原封不動(dòng)地返回這個(gè)實(shí)例。 - 參數(shù)
thenable
對象,它會將這個(gè)對象轉(zhuǎn)為Promise
對象,然后就立即執(zhí)行thenable
對象的then()
方法。
let thenable = { then: function(resolve, reject) { resolve(1); } };
- 參數(shù)是普通值,返回一個(gè)新的
Promise
對象,狀態(tài)為resolved
。
const promise = Promise.resolve(1); promise.then(function (value) { console.log(value) // 1 });
- 無參數(shù),直接返回一個(gè)
resolved
狀態(tài)的Promise
對象。
3.9 Promise.reject()
Promise.reject(reason)
方法也會返回一個(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; }); }
請求超時(shí)處理:
//請求 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)文章
解析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-02JS時(shí)間分片技術(shù)解決長任務(wù)導(dǎo)致的頁面卡頓
旨在把一個(gè)運(yùn)行時(shí)間比較長的任務(wù)分解成一塊一塊比較小的任務(wù),分塊去執(zhí)行,因?yàn)槌^ 50ms 的任務(wù)就會被認(rèn)為是 long task,用戶就能感知到渲染卡頓和交互的卡頓,所以我們可以縮短函數(shù)的連續(xù)執(zhí)行時(shí)間2022-07-07