不是吧?async/await異常捕獲你還在用try-catch~
寫(xiě)在前面
不知道大家項(xiàng)目里面是怎么處理 async/await 的異常,我斗膽在我們項(xiàng)目里翻了一下,發(fā)現(xiàn)大量使用 try-catch 來(lái)處理 async/await 異常。
try-awati-catch.png首先說(shuō)明一下, try-catch 處理并沒(méi)有什么問(wèn)題,我只是覺(jué)得這么寫(xiě)代碼會(huì)有點(diǎn)亂,感覺(jué)代碼邏輯像是斷層了一樣,不易理解;
其次是代碼冗余問(wèn)題,單個(gè) try-catch 就占了好幾行代碼,如果每個(gè)請(qǐng)求的地方都添加 try-catch,就會(huì)顯得代碼很臃腫。 而對(duì)于這種大量相同的冗余代碼,完全可以用一種通用的函數(shù)來(lái)替代。
async/await 是在 ES2017 中引入的,目的是為了讓異步操作更加直觀、方便,同時(shí)也解決了 Promise 的回調(diào)地獄問(wèn)題。想必這些概念大家都已經(jīng)了解了,那么我們?yōu)槭裁匆东@ async/await 的異常呢?它們是在什么時(shí)候發(fā)生異常呢? 帶著問(wèn)題我們一起看一下本文。
什么時(shí)候會(huì)請(qǐng)求異常
我們都知道 await 后面一般都是異步請(qǐng)求,異步請(qǐng)求發(fā)生異常的原因大致有以下幾種:
- 網(wǎng)絡(luò)問(wèn)題導(dǎo)致,網(wǎng)絡(luò)斷開(kāi)連接,請(qǐng)求不到;
- 網(wǎng)絡(luò)慢導(dǎo)致異步請(qǐng)求超時(shí)。
什么情況下需要處理請(qǐng)求異常
一旦有以上情況出現(xiàn),這個(gè)異步請(qǐng)求就會(huì)產(chǎn)生異常,而 JavaScript 又是一個(gè)單線程語(yǔ)言,代碼報(bào)錯(cuò)后就會(huì)導(dǎo)致后面的代碼無(wú)法繼續(xù)執(zhí)行,所以此時(shí)就需要添加 try-catch 來(lái)捕獲異步請(qǐng)求的異常,使得代碼可以繼續(xù)向后執(zhí)行。
但有必要為所有的異步請(qǐng)求都加 try-catch 嗎?
我研究了下我們項(xiàng)目的代碼,異步請(qǐng)求加了 try-catch 處理的,有以下幾種情況:
多個(gè)異步請(qǐng)求串行
try?{
?//?獲取列表list
?const?list?=?await?getList(params)
?//?獲取單個(gè)詳情
?const?info?=?await?getListById(list[0]?.id)
}?catch?{}
前一個(gè)異步請(qǐng)求的返回結(jié)果,會(huì)作為后一個(gè)異步請(qǐng)求的請(qǐng)求參數(shù)使用,所以一旦前一個(gè)請(qǐng)求異常,后面的請(qǐng)求肯定會(huì)異常,所以需要添加 try-catch 處理。
處理異步請(qǐng)求的 loading 狀態(tài)
loading.value?=?true
try?{
?//?獲取列表list
?const?list?=?await?getList(params)
}?finally?{
?loading.value?=?false
}
一般我們處理異步請(qǐng)求前,會(huì)為其添加 loading 狀態(tài),而一旦請(qǐng)求異常出現(xiàn)時(shí),如果不加 try-catch 時(shí)就會(huì)導(dǎo)致頁(yè)面一直處于 loading 狀態(tài)。所以需要在finally中將 loading 狀態(tài)置為 false,catch中處理時(shí)需要外部再處理一次。
那么,我們?nèi)绾蝺?yōu)雅的處理異步請(qǐng)求中的 try-catch 呢?
處理方法
使用 Promise 處理
首先需要明確一點(diǎn):正常情況下,await 命令后面是一個(gè) Promise 對(duì)象[1]。所以它本身就可以使用.catch來(lái)捕獲異常,那么像上面第二種只是處理 loading 狀態(tài)的操作,完全可以在.catch進(jìn)行處理,然后用if判斷來(lái)控制提前退出,沒(méi)必要寫(xiě) try-catch 這種冗余代碼。
loading.value?=?true
let?res?=?await?getList().catch(()?=>?(loading.value?=?false))
if?(!res)?return
//?請(qǐng)求成功后正常操作
await-to-js 處理函數(shù)
簡(jiǎn)單的異步請(qǐng)求我們可以使用上面這種方法,但遇到多個(gè)異步操作時(shí),就需要借助我們今天要說(shuō)的await-to-js[2]這個(gè)庫(kù),它的介紹很簡(jiǎn)單:無(wú)需 try-catch 即可輕松處理錯(cuò)誤。
而且源碼[3]賊簡(jiǎn)單,就 23 行代碼,我們一起來(lái)看看。
/**
?*?@param?{?Promise?}?promise
?*?@param?{?Object=?}?errorExt?-?Additional?Information?you?can?pass?to?the?err?object
?*?@return?{?Promise?}
?*/
export?function?to<T,?U?=?Error>(
??promise:?Promise<T>,
??errorExt?:?object,
):?Promise<[U,?undefined]?|?[null,?T]>?{
??return?(
????promise.then?<
????[null,?T]?>
????((data:?T)?=>?[null,?data]).catch?<
????[U,?undefined]?>
????((err:?U)?=>?{
??????if?(errorExt)?{
????????const?parsedError?=?Object.assign({},?err,?errorExt);
????????return?[parsedError,?undefined];
??????}
??????return?[err,?undefined];
????})
??);
}
export?default?to;
大致流程如下: 函數(shù)to接受參數(shù)Promise和errorExt,如果這個(gè) Promise 成功時(shí)返回[null, data],如果異常時(shí)會(huì)判斷是否帶有errorExt參數(shù)(代表傳遞給 err 對(duì)象的附加信息),如果有時(shí)會(huì)與 catch 捕獲的 err 合并返回,如果沒(méi)有時(shí)返回[err, undefined]。
很簡(jiǎn)單的邏輯是不是,接著我們看下它的用法:
- 安裝
#?use?npm
npm?i?await-to-js?--save
#?use?yarn
yarn?add?await-to-js
- 使用
首先引入to函數(shù),可以看到包很小,只有 370b,gzip 壓縮后只有 242b,所以放心引入,別擔(dān)心什么包大小問(wèn)題。
await-to-js-size.png我們通過(guò)to來(lái)改寫(xiě)一下上面第一種問(wèn)題:
import?to?from?'await-to-js';
//?獲取列表list
const?[err,?data]?=?await?to(getList(params));
if?(err)?return;
//?獲取單個(gè)詳情
const?info?=?await?to(getListById(list[0]?.id));
通過(guò)to函數(shù)改造后,如果返回第一個(gè)參數(shù)不為空時(shí),說(shuō)明該請(qǐng)求報(bào)錯(cuò),就可以提前 return 出去,如果不存在第一個(gè)參數(shù)時(shí),則異步請(qǐng)求成功。
總結(jié)
本文通過(guò)研究 async/await 的異常捕獲,發(fā)現(xiàn)了兩種常見(jiàn)的異步請(qǐng)求異常捕獲,并提出了兩種簡(jiǎn)單的解決方法。通過(guò)這兩種方法,就可以丟掉冗余的 try-catch,然后你就會(huì)發(fā)現(xiàn)沒(méi)了 try-catch 的代碼看起來(lái)都是順眼的。
很多小伙伴可能也會(huì)遇到這個(gè)問(wèn)題:盡管你提出了解決方案,但依舊會(huì)有項(xiàng)目組成員不用。你要這么想就錯(cuò)了,自己通過(guò)研究、查資料,最終學(xué)到了東西就足夠了,管別人干嘛!沒(méi)必要啊。
以上就是本文的全部?jī)?nèi)容,希望這篇文章對(duì)你有所幫助,歡迎點(diǎn)贊和收藏 ??,如果發(fā)現(xiàn)有什么錯(cuò)誤或者更好的解決方案及建議,歡迎隨時(shí)聯(lián)系。
參考資料
[1]https://es6.ruanyifeng.com/#docs/async#await-%E5%91%BD%E4%BB%A4
[2]https://github.com/scopsy/await-to-js
[3]https://github.com/scopsy/await-to-js/blob/master/src/await-to-js.ts
關(guān)于本文
作者:翔子丶https://juejin.cn/post/7224391827654180922最后
最后不要忘了點(diǎn)贊呦!
