淺談如何優(yōu)雅處理JavaScript異步錯誤

1. try/catch

公司主營業(yè)務(wù):成都網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊有機(jī)會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)推出屯留免費(fèi)做網(wǎng)站回饋大家。

try/catch基本上是大家最常和async/await一起使用的,基本上我們會用它去包圍大部分的異步方法。await關(guān)鍵字后面的promise一旦reject了,就會拋出一個異常錯誤。

run();
async function run() {
  try {
    await Promise.ject(new Error('Oops!'));
  } catch (err) {
    console.error(error.message);
  }
}

try/catch同樣也可以處理同步的錯誤,比如下面:

async function run() {
 const v = null;
 try {
  await Promise.resolve('foo');
  v.thisWillThrow;
 } catch (error) {
    // 會出現(xiàn)"TypeError: Cannot read property 'thisWillThrow' of null"
   console.error(error.message);
 }
}

好像我們只要無腦把邏輯都放到try/catch里面就萬事大吉了嗎?不太準(zhǔn)確,下面的代碼卻會導(dǎo)致unhandled promise rejection。這個return關(guān)鍵字直接返回就錯誤卻不會被捕獲:

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 來捕獲錯誤。

setTimeout(funciton() {
 try {
  fn()
 } catch (e) {
   // handle error
 }     
      
})

這是奏效的。 不過try catch會在各個地方。 V8引擎是不鼓勵try catch在函數(shù)中的使用的。 之前把try catch移到頂層來捕獲調(diào)用棧的錯誤,但這個對異步代碼不會奏效。

2. Golang-style(then)

golang style即使用.then()的方法來將一個promise轉(zhuǎn)換為另一個處理完錯誤的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);
  }
}

這么寫會直接拋出異常,因為這個方法拋出了異常,但是該方法本身沒有用try/catch捕獲。很多時候,我們在使用第三方庫的時候可能會出現(xiàn)這種情況。

then()解決方法

async function runAwait() {
    let err = await throwAnError().then(() => null, err => err);
  if (err){
    console.error(err.message);
  }
}

then()的方式,就會等待promise狀態(tài)resolve或reject后然后執(zhí)行相應(yīng)的回調(diào),然后判斷err對象并處理,所以其實它相當(dāng)于被捕獲了。

同時返回錯誤和值

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)返回一個數(shù)組,包含了結(jié)果和error對象。當(dāng)然如果是reject就會返回null和error對象;而如果resolved返回數(shù)組的第一個error對象就為null,第二個就是結(jié)果。

優(yōu)缺點(diǎn)

優(yōu)點(diǎn):這種模式可以更簡潔地處理,同時可以不需要寫catch。
缺點(diǎn)1:這是非常重復(fù)性的,每次執(zhí)行異步操作都需要去判斷error對象。
缺點(diǎn)2:無法幫助處理run方法中的同步錯誤。

所以這種方式需要謹(jǐn)慎使用。

3. Catch捕獲

上面兩種模式都可以處理異步錯誤,但是對于錯誤處理,最好的情況是在異步邏輯的最后加上catch,這樣可以保證所有錯誤都被捕獲到。其實這也是一個原則,即統(tǒng)一處理錯誤,而不是單獨(dú)去處理每個錯誤

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捕獲錯誤,如果handleError本身也有錯誤,就需要再catch一遍,但是為了避免回調(diào)地獄,如果該方法發(fā)生了錯誤就終止該進(jìn)程。

優(yōu)缺點(diǎn)

  • 使用catch的話,不管異步方法本身是否捕獲錯誤,它都會去捕獲異步錯誤。
  • 使用try/catch無法避免catch本身拋出異常,而如果它拋出了那除了嵌套多一層try/catch外,最好的做法就是加catch來讓代碼更簡潔。

4  全局錯誤捕獲

4.1 瀏覽器全局錯誤捕獲

瀏覽器全局處理基本上就是依靠事件,因為瀏覽器是事件驅(qū)動的。一旦拋出錯誤,解釋器在執(zhí)行環(huán)境上下文中停止執(zhí)行并展開,此時會有一個onerror全局事件拋出:

window.addEventListener('error', function (e) {
  var error = e.error;
  console.log(error);
})

全局錯誤處理器會捕獲任何在執(zhí)行環(huán)境中發(fā)生的錯誤,即便是不同的對象發(fā)生的錯誤事件,或者是各種類型的錯誤。這是全局集中處理錯誤的一種常見方式。

調(diào)用棧

調(diào)用棧在定位問題的時候十分重要,我們可以使用調(diào)用棧在處理器中處理特定的錯誤。

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ā)了什么錯誤。在調(diào)試時調(diào)用堆棧也會非常有用。你 可以分析log,看到什么條件下觸發(fā)了錯誤。

淺談如何優(yōu)雅處理JavaScript異步錯誤

注意:

如果跨域腳本是不會看到錯誤的。 在JS中,錯誤信息僅僅是允許在同一個域中。

個人想法

更多的時候,代碼拋出了異常,我們更關(guān)注的是在運(yùn)行時,某個變量的值是什么,是否這個變量的值導(dǎo)致了錯誤,所以打印出調(diào)用時的跟多的信息更重要。

4.2 Node.js全局錯誤捕獲

Node.js本身的異常處理要復(fù)雜得多,因為涉及到了進(jìn)程或線程拋出異常的問題。

基于Koa的全局錯誤處理

nodejs是error-first的異步處理機(jī)制,此處底層會調(diào)用net模塊的listen方法并在錯誤發(fā)生時執(zhí)行回調(diào)。

app.listen(app.config.listenPort, (err) => {
 if (err) throw err
 app.logger.info(`> Ready on http://localhost:${app.config.listenPort}`)
})

路由錯誤處理

對于每個路由,它可能也會有不同的錯誤處理邏輯,這時路由進(jìn)來的請求就需要根據(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é)

  1. 通常異??赡苁穷A(yù)期的或者超出預(yù)期的,不管怎樣,使用try/catch沒有問題。
  2. 對于超出預(yù)期的錯誤,盡量使用catch來保證它們會被捕獲到。
  3. 把錯誤處理器添加到window對象上,它會捕獲到異步錯誤,符合了DRY和SOLID原則。一個全局的錯誤處理器可以幫你保持異步代碼整潔。

Reference

async-await-error-handling
nodejs-v12-lts

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

分享標(biāo)題:淺談如何優(yōu)雅處理JavaScript異步錯誤
URL鏈接:http://bm7419.com/article6/gocgog.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google、靜態(tài)網(wǎng)站、網(wǎng)站建設(shè)、手機(jī)網(wǎng)站建設(shè)、虛擬主機(jī)品牌網(wǎng)站設(shè)計

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

營銷型網(wǎng)站建設(shè)