您好,登錄后才能下訂單哦!
1. try/catch
try/catch基本上是大家最常和async/await一起使用的,基本上我們會用它去包圍大部分的異步方法。await關鍵字后面的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) { // 會出現"TypeError: Cannot read property 'thisWillThrow' of null" console.error(error.message); } }
好像我們只要無腦把邏輯都放到try/catch里面就萬事大吉了嗎?不太準確,下面的代碼卻會導致unhandled promise rejection。這個return關鍵字直接返回就錯誤卻不會被捕獲:
async function run() { try { // 直接返回Promise,而不是用await關鍵字 return Promise.reject(new Error('Oops!')); } catch (error) { console.error(error.message); } }
一種處理方式是使用return await來解決。
try catch捕獲不了回調函數。try catch 僅僅在單一執行環境中奏效。是在回調中加入try catch 來捕獲錯誤。
setTimeout(funciton() { try { fn() } catch (e) { // handle error } })
這是奏效的。 不過try catch會在各個地方。 V8引擎是不鼓勵try catch在函數中的使用的。 之前把try catch移到頂層來捕獲調用棧的錯誤,但這個對異步代碼不會奏效。
2. Golang-style(then)
golang style即使用.then()的方法來將一個promise轉換為另一個處理完錯誤的reject promise。可以使用類似if(err)來進行檢查:
async function throwAnError() { throw new Error('Opps!'); } async function runAwait() { let err = await throwAnError(); if (err){ console.error(err.message); } }
這么寫會直接拋出異常,因為這個方法拋出了異常,但是該方法本身沒有用try/catch捕獲。很多時候,我們在使用第三方庫的時候可能會出現這種情況。
then()解決方法
async function runAwait() { let err = await throwAnError().then(() => null, err => err); if (err){ console.error(err.message); } }
then()的方式,就會等待promise狀態resolve或reject后然后執行相應的回調,然后判斷err對象并處理,所以其實它相當于被捕獲了。
同時返回錯誤和值
async function run() { let [err, res] = await throwAnError().then(v => [null, v], err => [err, null]); if (err){ console.error(err.message); } console.log(res) }
結果:這么做可以通過解構返回一個數組,包含了結果和error對象。當然如果是reject就會返回null和error對象;而如果resolved返回數組的第一個error對象就為null,第二個就是結果。
優缺點
優點:這種模式可以更簡潔地處理,同時可以不需要寫catch。
缺點1:這是非常重復性的,每次執行異步操作都需要去判斷error對象。
缺點2:無法幫助處理run方法中的同步錯誤。
所以這種方式需要謹慎使用。
3. Catch捕獲
上面兩種模式都可以處理異步錯誤,但是對于錯誤處理,最好的情況是在異步邏輯的最后加上catch,這樣可以保證所有錯誤都被捕獲到。其實這也是一個原則,即統一處理錯誤,而不是單獨去處理每個錯誤。
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一遍,但是為了避免回調地獄,如果該方法發生了錯誤就終止該進程。
優缺點
4 全局錯誤捕獲
4.1 瀏覽器全局錯誤捕獲
瀏覽器全局處理基本上就是依靠事件,因為瀏覽器是事件驅動的。一旦拋出錯誤,解釋器在執行環境上下文中停止執行并展開,此時會有一個onerror全局事件拋出:
window.addEventListener('error', function (e) { var error = e.error; console.log(error); })
全局錯誤處理器會捕獲任何在執行環境中發生的錯誤,即便是不同的對象發生的錯誤事件,或者是各種類型的錯誤。這是全局集中處理錯誤的一種常見方式。
調用棧
調用棧在定位問題的時候十分重要,我們可以使用調用棧在處理器中處理特定的錯誤。
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); });
通過日志可以看到,具體什么情況觸發了什么錯誤。在調試時調用堆棧也會非常有用。你 可以分析log,看到什么條件下觸發了錯誤。
注意:
如果跨域腳本是不會看到錯誤的。 在JS中,錯誤信息僅僅是允許在同一個域中。
個人想法
更多的時候,代碼拋出了異常,我們更關注的是在運行時,某個變量的值是什么,是否這個變量的值導致了錯誤,所以打印出調用時的跟多的信息更重要。
4.2 Node.js全局錯誤捕獲
Node.js本身的異常處理要復雜得多,因為涉及到了進程或線程拋出異常的問題。
基于Koa的全局錯誤處理
nodejs是error-first的異步處理機制,此處底層會調用net模塊的listen方法并在錯誤發生時執行回調。
app.listen(app.config.listenPort, (err) => { if (err) throw err app.logger.info(`> Ready on http://localhost:${app.config.listenPort}`) })
路由錯誤處理
對于每個路由,它可能也會有不同的錯誤處理邏輯,這時路由進來的請求就需要根據情況返回不同的異常碼和信息。
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. 總結
Reference
async-await-error-handling
nodejs-v12-lts
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。