Node端異常捕獲的實(shí)現(xiàn)方法
常見Node報(bào)錯處理機(jī)制
try catch
try...catch是大家最常用的錯誤處理機(jī)制,Javascript語言內(nèi)置的錯誤處理機(jī)制可以在檢測到代碼異常的時(shí)候直接進(jìn)行捕獲并處理。
function test() {
try {
throw new Error("error");
} catch(e) {
console.log(e);
} finally {
console.log("finally");
}
}
test()
一般來說,throw 用于拋出異常,但是單純從語言的角度,我們可以拋出任何值,也不一定是異常邏輯,但是為了保證語義清晰,不建議用 throw 表達(dá)任何非異常邏輯。try 語句用于捕獲異常,用 throw 拋出的異常,可以在 try 語句的結(jié)構(gòu)中被處理掉:try 部分用于標(biāo)識捕獲異常的代碼段,catch 部分則用于捕獲異常后做一些處理,而 finally 則是用于執(zhí)行后做一些必須執(zhí)行的清理工作。catch 結(jié)構(gòu)會創(chuàng)建一個(gè)局部的作用域,并且把一個(gè)變量寫入其中,需要注意,在這個(gè)作用域,不能再聲明變量 e 了,否則會出錯。在 catch 中重新拋出錯誤的情況非常常見,在設(shè)計(jì)比較底層的函數(shù)時(shí),常常會這樣做,保證拋出的錯誤能被理解。finally 語句一般用于釋放資源,它一定會被執(zhí)行,我們在前面的課程中已經(jīng)討論過一些 finally 的特征,即使在 try 中出現(xiàn)了 return,finally 中的語句也一定要被執(zhí)行。
Node原生錯誤處理機(jī)制
大多數(shù)Node.js核心API都提供的是利用回調(diào)函數(shù)處理錯誤,之所以采用這種錯誤處理機(jī)制,是因?yàn)楫惒椒椒ㄋa(chǎn)生的方法并不能簡單地通過try...catch機(jī)制進(jìn)行攔截。
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是用于處理異步調(diào)用的規(guī)范,由于 JavaScript 特殊的 EventLoop 機(jī)制,由 Promise 異步產(chǎn)生錯誤是沒有辦法使用 try...catch 的。
Promise提供的錯誤處理機(jī)制,是通過catch方法進(jìn)行捕獲。
try {
Promise.reject()
} catch(err) {
// 這里啥都 catch 不到
console.log(err)
}
fs.copy(
buildStatic,
aresStatic
).then(() => {
console.log(`${buildStatic} -> ${aresStatic}`)
}).catch(err => {
// 這里可以捕獲到報(bào)錯
console.log(err)
})
async/await + try catch
async/await語法糖加上try...catch語句進(jìn)行的。這樣做的好處是異步和同步調(diào)用都能夠使用統(tǒng)一的方式進(jìn)行處理了。
對于異步代碼,建議統(tǒng)一轉(zhuǎn)換成Promise然后采用async/await + try...catch這種方式進(jìn)行處理。這樣風(fēng)格統(tǒng)一,程序的健壯性也大大加強(qiáng)。
async function one() {
// a未定義
a.b = 3
}
async function test() {
try {
await one();
} catch(error) {
// a is not defined
console.log(error);
}
}
test();
unhandledRejection
實(shí)際開發(fā)中,總是會有一些 Promise 被遺漏掉catch處理,沒有得到錯誤處理,會導(dǎo)致應(yīng)用crash。我們可以通過**unhandledrejection** 事件捕獲未處理的 Promise 錯誤。
實(shí)現(xiàn)原理:Node.js 會在每次 Tick 執(zhí)行完后檢查是否有未捕獲的錯誤 Promise,如果有,則觸發(fā) unhandledRejection事件。
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at:', p, 'reason:', reason);
});
特殊情況如何捕獲異常
如果是回調(diào)函數(shù)中捕獲異常怎么做?用domain去捕獲,domian捕獲會拋出500錯誤,但是domain捕獲有一個(gè)問題,會丟失棧信息,無法保證程序健康進(jìn)行,所以要結(jié)束進(jìn)程,在回調(diào)函數(shù)中process.exit(1),然后用node的server.close方法再去釋放,server.close連接釋放后自動結(jié)束進(jìn)程,所以不用在server.close中去結(jié)束進(jìn)程process.exit(1)uncaughtExpection捕獲異常的的原理就是:uncaughtExpection事件存在回調(diào)函數(shù)process.on("uncaughtExpection",callback)時(shí)node不會強(qiáng)制結(jié)束進(jìn)程,這樣可彌補(bǔ)domain丟失stack的問題
所以domian去捕獲絕大部分回調(diào)函數(shù)中的異常,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 進(jìn)程的一個(gè)事件。如果進(jìn)程里產(chǎn)生了一個(gè)異常而沒有被任何Try Catch捕獲會觸發(fā)這個(gè)事件。
NodeJS 對于未捕獲異常的默認(rèn)處理是:
- 觸發(fā) uncaughtException 事件
- 如果 uncaughtException 沒有被監(jiān)聽
- 打印異常的堆棧信息
- 觸發(fā)進(jìn)程的 exit 事件
所以如果某個(gè)報(bào)錯沒有被任意try catch捕獲,且沒有定義uncaughtException事件,就會導(dǎo)致程序退出。
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason)
})
Express錯誤處理
Express中,路由或中間件報(bào)錯處理可以通過特殊的中間件來完成。
一般中間件的參數(shù)為3個(gè):req,res, next。如果你use一個(gè)4個(gè)參數(shù)的中間件,它將被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那個(gè)錯誤中間件,如果不用next就不會傳遞到全局錯誤處理中間件
//如果在/a錯誤處理中間件里調(diào)用next(err) 那么全局錯誤中間件也會被執(zhí)行到此這篇關(guān)于Node端異常捕獲的實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)Node 異常捕獲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用nodejs?+?koa?+?typescript?集成和自動重啟的問題
這篇文章主要介紹了nodejs?+?koa?+?typescript?集成和自動重啟,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12
node將對象轉(zhuǎn)化為query的實(shí)現(xiàn)方法
本文主要介紹了node將對象轉(zhuǎn)化為query的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
詳解Node中導(dǎo)入模塊require和import的區(qū)別
本篇文章主要介紹了詳解Node中導(dǎo)入模塊require和import的區(qū)別,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-08-08
node.js基于fs模塊對系統(tǒng)文件及目錄進(jìn)行讀寫操作的方法詳解
這篇文章主要介紹了node.js基于fs模塊對系統(tǒng)文件及目錄進(jìn)行讀寫操作的方法,結(jié)合實(shí)例形式分析了nodejs使用fs模塊針對文件與目錄的讀寫、創(chuàng)建、刪除等相關(guān)操作技巧,需要的朋友可以參考下2017-11-11
nodejs與瀏覽器中全局對象區(qū)別點(diǎn)總結(jié)
在本篇文章里小編給大家整理的是一篇關(guān)于nodejs與瀏覽器中全局對象區(qū)別點(diǎn)總結(jié)內(nèi)容,對此有需要的朋友們可以學(xué)習(xí)下。2021-12-12
nodejs結(jié)合Socket.IO實(shí)現(xiàn)的即時(shí)通訊功能詳解
這篇文章主要介紹了nodejs結(jié)合Socket.IO實(shí)現(xiàn)的即時(shí)通訊功能,結(jié)合實(shí)例形式詳細(xì)分析了nodejs結(jié)合Socket.IO實(shí)現(xiàn)即時(shí)通訊的相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2018-01-01
node.js中的events.emitter.listeners方法使用說明
這篇文章主要介紹了node.js中的events.emitter.listeners方法使用說明,本文介紹了events.emitter.listeners 的方法說明、語法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12
基于Koa(nodejs框架)對json文件進(jìn)行增刪改查的示例代碼
這篇文章主要介紹了基于Koa(nodejs框架)對json文件進(jìn)行增刪改查的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02

