淺談如何優(yōu)雅處理JavaScript異步錯(cuò)誤
1. try/catch
try/catch基本上是大家最常和async/await一起使用的,基本上我們會(huì)用它去包圍大部分的異步方法。await關(guān)鍵字后面的promise一旦reject了,就會(huì)拋出一個(gè)異常錯(cuò)誤。
run();
async function run() {
try {
await Promise.ject(new Error('Oops!'));
} catch (err) {
console.error(error.message);
}
}
try/catch同樣也可以處理同步的錯(cuò)誤,比如下面:
async function run() {
const v = null;
try {
await Promise.resolve('foo');
v.thisWillThrow;
} catch (error) {
// 會(huì)出現(xiàn)"TypeError: Cannot read property 'thisWillThrow' of null"
console.error(error.message);
}
}
好像我們只要無腦把邏輯都放到try/catch里面就萬事大吉了嗎?不太準(zhǔn)確,下面的代碼卻會(huì)導(dǎo)致unhandled promise rejection。這個(gè)return關(guān)鍵字直接返回就錯(cuò)誤卻不會(huì)被捕獲:
async function run() {
try {
// 直接返回Promise,而不是用await關(guān)鍵字
return Promise.reject(new Error('Oops!'));
} catch (error) {
console.error(error.message);
}
}
一種處理方式是使用return await來解決。
try catch捕獲不了回調(diào)函數(shù)。try catch 僅僅在單一執(zhí)行環(huán)境中奏效。是在回調(diào)中加入try catch 來捕獲錯(cuò)誤。
setTimeout(funciton() {
try {
fn()
} catch (e) {
// handle error
}
})
這是奏效的。 不過try catch會(huì)在各個(gè)地方。 V8引擎是不鼓勵(lì)try catch在函數(shù)中的使用的。 之前把try catch移到頂層來捕獲調(diào)用棧的錯(cuò)誤,但這個(gè)對(duì)異步代碼不會(huì)奏效。
2. Golang-style(then)
golang style即使用.then()的方法來將一個(gè)promise轉(zhuǎn)換為另一個(gè)處理完錯(cuò)誤的reject promise??梢允褂妙愃苅f(err)來進(jìn)行檢查:
async function throwAnError() {
throw new Error('Opps!');
}
async function runAwait() {
let err = await throwAnError();
if (err){
console.error(err.message);
}
}
這么寫會(huì)直接拋出異常,因?yàn)檫@個(gè)方法拋出了異常,但是該方法本身沒有用try/catch捕獲。很多時(shí)候,我們?cè)谑褂玫谌綆斓臅r(shí)候可能會(huì)出現(xiàn)這種情況。
then()解決方法
async function runAwait() {
let err = await throwAnError().then(() => null, err => err);
if (err){
console.error(err.message);
}
}
then()的方式,就會(huì)等待promise狀態(tài)resolve或reject后然后執(zhí)行相應(yīng)的回調(diào),然后判斷err對(duì)象并處理,所以其實(shí)它相當(dāng)于被捕獲了。
同時(shí)返回錯(cuò)誤和值
async function run() {
let [err, res] = await throwAnError().then(v => [null, v], err => [err, null]);
if (err){
console.error(err.message);
}
console.log(res)
}
結(jié)果:這么做可以通過解構(gòu)返回一個(gè)數(shù)組,包含了結(jié)果和error對(duì)象。當(dāng)然如果是reject就會(huì)返回null和error對(duì)象;而如果resolved返回?cái)?shù)組的第一個(gè)error對(duì)象就為null,第二個(gè)就是結(jié)果。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):這種模式可以更簡(jiǎn)潔地處理,同時(shí)可以不需要寫catch。
缺點(diǎn)1:這是非常重復(fù)性的,每次執(zhí)行異步操作都需要去判斷error對(duì)象。
缺點(diǎn)2:無法幫助處理run方法中的同步錯(cuò)誤。
所以這種方式需要謹(jǐn)慎使用。
3. Catch捕獲
上面兩種模式都可以處理異步錯(cuò)誤,但是對(duì)于錯(cuò)誤處理,最好的情況是在異步邏輯的最后加上catch,這樣可以保證所有錯(cuò)誤都被捕獲到。其實(shí)這也是一個(gè)原則,即統(tǒng)一處理錯(cuò)誤,而不是單獨(dú)去處理每個(gè)錯(cuò)誤。
async function run() {
return Promise.reject(new Error('Oops!'));
}
run().catch(function handleError(err) {
console.error(err.message);
}).catch( err => {
process.nextTick(() => { throw errl});
})
使用catch捕獲錯(cuò)誤,如果handleError本身也有錯(cuò)誤,就需要再catch一遍,但是為了避免回調(diào)地獄,如果該方法發(fā)生了錯(cuò)誤就終止該進(jìn)程。
優(yōu)缺點(diǎn)
- 使用catch的話,不管異步方法本身是否捕獲錯(cuò)誤,它都會(huì)去捕獲異步錯(cuò)誤。
- 使用try/catch無法避免catch本身拋出異常,而如果它拋出了那除了嵌套多一層try/catch外,最好的做法就是加catch來讓代碼更簡(jiǎn)潔。
4 全局錯(cuò)誤捕獲
4.1 瀏覽器全局錯(cuò)誤捕獲
瀏覽器全局處理基本上就是依靠事件,因?yàn)闉g覽器是事件驅(qū)動(dòng)的。一旦拋出錯(cuò)誤,解釋器在執(zhí)行環(huán)境上下文中停止執(zhí)行并展開,此時(shí)會(huì)有一個(gè)onerror全局事件拋出:
window.addEventListener('error', function (e) {
var error = e.error;
console.log(error);
})
全局錯(cuò)誤處理器會(huì)捕獲任何在執(zhí)行環(huán)境中發(fā)生的錯(cuò)誤,即便是不同的對(duì)象發(fā)生的錯(cuò)誤事件,或者是各種類型的錯(cuò)誤。這是全局集中處理錯(cuò)誤的一種常見方式。
調(diào)用棧
調(diào)用棧在定位問題的時(shí)候十分重要,我們可以使用調(diào)用棧在處理器中處理特定的錯(cuò)誤。
window.addEventListener('error', function (e) {
var stack = e.error.stack;
var message = e.error.toString();
if (stack) {
message += '\n' + stack;
}
var xhr = new XMLHttpRequest();
xhr.open('POST', '/log', true);
// Fire an Ajax request with error details
xhr.send(message);
});
通過日志可以看到,具體什么情況觸發(fā)了什么錯(cuò)誤。在調(diào)試時(shí)調(diào)用堆棧也會(huì)非常有用。你 可以分析log,看到什么條件下觸發(fā)了錯(cuò)誤。

注意:
如果跨域腳本是不會(huì)看到錯(cuò)誤的。 在JS中,錯(cuò)誤信息僅僅是允許在同一個(gè)域中。
個(gè)人想法
更多的時(shí)候,代碼拋出了異常,我們更關(guān)注的是在運(yùn)行時(shí),某個(gè)變量的值是什么,是否這個(gè)變量的值導(dǎo)致了錯(cuò)誤,所以打印出調(diào)用時(shí)的跟多的信息更重要。
4.2 Node.js全局錯(cuò)誤捕獲
Node.js本身的異常處理要復(fù)雜得多,因?yàn)樯婕暗搅诉M(jìn)程或線程拋出異常的問題。
基于Koa的全局錯(cuò)誤處理
nodejs是error-first的異步處理機(jī)制,此處底層會(huì)調(diào)用net模塊的listen方法并在錯(cuò)誤發(fā)生時(shí)執(zhí)行回調(diào)。
app.listen(app.config.listenPort, (err) => {
if (err) throw err
app.logger.info(`> Ready on http://localhost:${app.config.listenPort}`)
})
路由錯(cuò)誤處理
對(duì)于每個(gè)路由,它可能也會(huì)有不同的錯(cuò)誤處理邏輯,這時(shí)路由進(jìn)來的請(qǐng)求就需要根據(jù)情況返回不同的異常碼和信息。
router.get('/loginAuth', async (ctx, next) => {
try {
const code = query.code
const res = await requestToken(code)
if (res.data.code !== 0) {
ctx.app.logger.error(`request token error.Code is ${res.data.code} || response is: ${JSON.stringify(res.data.data)} || msg: ${res.data.message}`)
ctx.body = {
code: 10000,
message: `request token by code error`
}
} else {
ctx.body = res.data
}
} catch (err) {
ctx.app.logger.error(`request api has exception ${ctx.request.url} || ${err.code} || ${err.message} || ${err.stack}`)
ctx.body = {
code: 500,
message: `Error response`
}
}
})
5. 總結(jié)
- 通常異??赡苁穷A(yù)期的或者超出預(yù)期的,不管怎樣,使用try/catch沒有問題。
- 對(duì)于超出預(yù)期的錯(cuò)誤,盡量使用catch來保證它們會(huì)被捕獲到。
- 把錯(cuò)誤處理器添加到window對(duì)象上,它會(huì)捕獲到異步錯(cuò)誤,符合了DRY和SOLID原則。一個(gè)全局的錯(cuò)誤處理器可以幫你保持異步代碼整潔。
Reference
async-await-error-handling
nodejs-v12-lts
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
JavaScript實(shí)現(xiàn)內(nèi)容滾動(dòng)與導(dǎo)航標(biāo)簽互動(dòng)關(guān)聯(lián)方案
這篇文章主要介紹了JavaScript實(shí)現(xiàn)內(nèi)容滾動(dòng)與導(dǎo)航標(biāo)簽互動(dòng)關(guān)聯(lián)方案,主要根據(jù)滾動(dòng)左側(cè)內(nèi)容,關(guān)聯(lián)激活右側(cè)導(dǎo)航節(jié)點(diǎn)展開相應(yīng)介紹,需要的小伙伴可以參考一下2022-06-06
Bootstrap組合上、下拉框簡(jiǎn)單實(shí)現(xiàn)代碼
這篇文章主要介紹了Bootstrap組合上、下拉框的簡(jiǎn)單實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
zepto.js 實(shí)時(shí)監(jiān)聽輸入框的方法
今天小編就為大家分享一篇zepto.js 實(shí)時(shí)監(jiān)聽輸入框的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-12-12
JS實(shí)現(xiàn)固定時(shí)間點(diǎn)執(zhí)行某任務(wù)的代碼示例
在Web前端開發(fā)中,有時(shí)我們需要在特定的時(shí)間點(diǎn)執(zhí)行某些任務(wù),例如每日定時(shí)發(fā)送數(shù)據(jù)報(bào)告、每小時(shí)更新一次用戶界面等,JavaScript 提供了多種方法來實(shí)現(xiàn)這一需求,本文將詳細(xì)介紹如何使用這些工具和技術(shù),并通過豐富的代碼示例展示其具體應(yīng)用,需要的朋友可以參考下2025-02-02
js判斷請(qǐng)求的url是否可訪問,支持跨域判斷的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄猨s判斷請(qǐng)求的url是否可訪問,支持跨域判斷的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-09-09
JS拖拽排序插件Sortable.js用法實(shí)例分析
這篇文章主要介紹了JS拖拽排序插件Sortable.js用法,結(jié)合實(shí)例形式分析了拖拽排序插件Sortable.js功能、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-02-02

