Node端異常捕獲的實現方法
常見Node報錯處理機制
try catch
try…catch是大傢最常用的錯誤處理機制,Javascript語言內置的錯誤處理機制可以在檢測到代碼異常的時候直接進行捕獲並處理。
function test() { try { throw new Error("error"); } catch(e) { console.log(e); } finally { console.log("finally"); } } test()
一般來說,throw 用於拋出異常,但是單純從語言的角度,我們可以拋出任何值,也不一定是異常邏輯,但是為瞭保證語義清晰,不建議用 throw 表達任何非異常邏輯。try 語句用於捕獲異常,用 throw 拋出的異常,可以在 try 語句的結構中被處理掉:try 部分用於標識捕獲異常的代碼段,catch 部分則用於捕獲異常後做一些處理,而 finally 則是用於執行後做一些必須執行的清理工作。catch 結構會創建一個局部的作用域,並且把一個變量寫入其中,需要註意,在這個作用域,不能再聲明變量 e 瞭,否則會出錯。在 catch 中重新拋出錯誤的情況非常常見,在設計比較底層的函數時,常常會這樣做,保證拋出的錯誤能被理解。finally 語句一般用於釋放資源,它一定會被執行,我們在前面的課程中已經討論過一些 finally 的特征,即使在 try 中出現瞭 return,finally 中的語句也一定要被執行。
Node原生錯誤處理機制
大多數Node.js核心API都提供的是利用回調函數處理錯誤,之所以采用這種錯誤處理機制,是因為異步方法所產生的方法並不能簡單地通過try…catch機制進行攔截。
const fs = require('fs'); function read() { fs.readFile("/some/file/does-not-exist", (err, data) => { if(err) { throw new Error("file not exist"); } console.log(data); }); } read();
Promise
Promise是用於處理異步調用的規范,由於 JavaScript 特殊的 EventLoop 機制,由 Promise 異步產生錯誤是沒有辦法使用 try...catch
的。
Promise提供的錯誤處理機制,是通過catch方法進行捕獲。
try { Promise.reject() } catch(err) { // 這裡啥都 catch 不到 console.log(err) }
fs.copy( buildStatic, aresStatic ).then(() => { console.log(`${buildStatic} -> ${aresStatic}`) }).catch(err => { // 這裡可以捕獲到報錯 console.log(err) })
async/await + try catch
async/await語法糖加上try…catch語句進行的。這樣做的好處是異步和同步調用都能夠使用統一的方式進行處理瞭。
對於異步代碼,建議統一轉換成Promise然後采用async/await + try…catch這種方式進行處理。這樣風格統一,程序的健壯性也大大加強。
async function one() { // a未定義 a.b = 3 } async function test() { try { await one(); } catch(error) { // a is not defined console.log(error); } } test();
unhandledRejection
實際開發中,總是會有一些 Promise 被遺漏掉catch處理,沒有得到錯誤處理,會導致應用crash。我們可以通過**unhandledrejection
** 事件捕獲未處理的 Promise 錯誤。
實現原理:Node.js 會在每次 Tick 執行完後檢查是否有未捕獲的錯誤 Promise,如果有,則觸發 unhandledRejection
事件。
process.on('unhandledRejection', (reason, p) => { console.log('Unhandled Rejection at:', p, 'reason:', reason); });
特殊情況如何捕獲異常
如果是回調函數中捕獲異常怎麼做?用domain去捕獲,domian捕獲會拋出500錯誤,但是domain捕獲有一個問題,會丟失棧信息,無法保證程序健康進行,所以要結束進程,在回調函數中process.exit(1)
,然後用node的server.close方法再去釋放,server.close連接釋放後自動結束進程,所以不用在server.close中去結束進程process.exit(1)
uncaughtExpection捕獲異常的的原理就是:uncaughtExpection事件存在回調函數process.on("uncaughtExpection",callback)
時node不會強制結束進程,這樣可彌補domain丟失stack的問題
所以domian去捕獲絕大部分回調函數中的異常,uncaughtExpection去捕獲丟失stack異常,這樣就完整瞭
app.use(function(req,res,next){ var reqDomain = domain.create(); reqDomain.on("err",function(){ try { var killTimer = setTimeout(function(){ process.exit(1); },1000) killTimer.unref(); server.close(); res.send(500); } catch(e) { // statements console.log(e.stack); } }) reqDomain.run(next); }); process.on("uncaughtException",function(err){ console.log(err); try{ var killTimer = setTimeout(function(){ process.exit(1) },1000) killTimer.unref(); server.close(); }catch(e){ console.log(e.stack); } });
uncaughtException
uncaughtException
也是 NodeJS 進程的一個事件。如果進程裡產生瞭一個異常而沒有被任何Try Catch
捕獲會觸發這個事件。
NodeJS 對於未捕獲異常的默認處理是:
– 觸發 uncaughtException
事件
– 如果 uncaughtException 沒有被監聽
– 打印異常的堆棧信息
– 觸發進程的 exit 事件
所以如果某個報錯沒有被任意try catch捕獲,且沒有定義uncaughtException事件,就會導致程序退出。
process.on('unhandledRejection', (reason, p) => { console.log('Unhandled Rejection at: Promise', p, 'reason:', reason) })
Express錯誤處理
Express中,路由或中間件報錯處理可以通過特殊的中間件來完成。
一般中間件的參數為3個:req
,res
, next
。如果你use
一個4個參數的中間件,它將被Express視為錯誤處理中間件。
app.get('/a',function(req,res,next){ res.end('hahah'); next(new Error('錯誤啦')); }); app.use('/a',function(err,req,res,next){ console.log('路由錯誤'+err); }) //all error中間件 app.use(function(err, req, res, next) { console.log("Error happens", err.stack); }); //錯誤傳遞,/a的錯誤處理首先匹配/a那個錯誤中間件,如果不用next就不會傳遞到全局錯誤處理中間件 //如果在/a錯誤處理中間件裡調用next(err) 那麼全局錯誤中間件也會被執行
到此這篇關於Node端異常捕獲的實現方法的文章就介紹到這瞭,更多相關Node 異常捕獲內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- node.js express和koa中間件機制和錯誤處理機制
- 詳細談談NodeJS進程是如何退出的
- 詳解JavaScript錯誤捕獲
- JS異步代碼單元測試之神奇的Promise
- node事件循環中事件執行的順序