node是對錯誤處理要求比較高的語言,假如對錯誤處理沒有到位可能會形成程序進程退出javascript
錯誤處理是程序中一個重要的部分,也是判斷你的程序是否專業的標準。通常來講咱們寫程序的時候都會選擇使用try...catch來進行錯誤捕獲,或者有時候咱們會使用throw進行錯誤拋出,這是都是經常使用的錯誤捕獲方法。可是咱們在進行node進行開發的時候就會接觸到異步過程的中的錯誤處理。java
咱們知道在node開發的時候會運用到不少第三方的模塊,好比咱們常常會使用最大的包管理工具npm,裏面下載的包都會放到咱們項目當中的node_modules裏面,咱們打開能夠看到裏面包含的文件不少,代碼量也是巨大的。這裏面就會有不少的bug隱患在裏面,這時候使用錯誤捕獲就很是有用了。node
其實咱們一開始想到的就是在全局範圍內進行錯誤的監聽,node提供了一個uncaughtException捕獲異常,可是這種方法咱們會難以定位到錯誤的發生位置。不該該把該函數當成萬能的捕獲模塊,而是最後的解決方案。web
Error定義了Node中常見的錯誤類型,咱們可使用Error進行錯誤的拋出。Error模塊裏面包含了一個堆棧軌跡用於描述Error是從哪裏產生的,通常來講咱們能夠準確知道錯誤發生在哪一部分的代碼當中,根據錯誤的描述信息能夠快速定位到錯誤。npm
var fs = require("fs");
fs.readFile("file",function(err,data){
if(err){
throw new Error("Error!")
}
})
複製代碼
Node程序中產生的全部Error都是使用Error類的實例或者繼承自Error類。咱們在程序代碼不中不只可使用回調函數自帶的Error模塊,並且咱們能夠顯示第捕獲錯誤。好比當你知道邏輯代碼運行都某一部分是不對的,應該進行錯誤的捕獲和提醒,你就可使用:json
throw new Error("自定義錯誤信息!")
複製代碼
接下來就簡單介紹一下Node中咱們是如何進行錯誤捕獲的,總的來講咱們能夠有如下三種方式,try/catch、callback、event。以前咱們經常使用的try/catch方式只適用於同步的調用狀況,可是咱們知道node中會出現不少的異步調用方式。promise
首先咱們應該瞭解的是在異步操做當中該方法是沒法捕獲錯誤的,主要緣由就是由於異步調用返回時,代碼的上下文已經改變,回調函數當中的代碼已經脫離了try/catch的範圍,因此是沒法捕獲的。dom
同步調用狀況:異步
//這裏能夠捕獲
try{
throw new Error("這裏出錯了!");
}catch(e){
console.log(e)
}
複製代碼
異步調用狀況:async
try{
setTimeout(function(){throw new Error('這裏出錯了!')},1000)
}catch(e){
console.log(e);//這裏沒法進行捕獲
}
複製代碼
回調函數的方式主要是經過參數的判斷來肯定的,node中一般回調函數都會接受兩個參數error和result。這兩個值確定會有一個不爲空,咱們經過讀取本地文件的操做來舉一個例子。(由於方法返回的是buffer對象難以閱讀,咱們就是使用utf8進行讀取,最後字符串轉成json)
var fs = require("fs");
fs.readFile("./a.json",'utf8', function(error, result) {
if (error) {
console.log(error);
return;
}
console.log(JSON.parse(result));
});
複製代碼
假如文件存在就會返回輸出結果,故意寫成a1.json不存在就會拋出錯誤:
{ [Error: ENOENT: no such file or directory, open 'D:\test\a1.json']
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'D:\\test\\a1.json' }
複製代碼
咱們進行對文件流監聽的時候,即便文件流讀取是一個同步的方法,可是咱們依舊不能使用try/catch來捕獲,爲何呢?由於該方法返回了一個對象,只能使用事件處理的方式來處理異常。若是使用try/catch的話直接報錯退出,使用事件監聽的方式就不會影響程序的運行且會報出錯誤信息。
因此正確的方式應該是這樣的:
var fs = require("fs");
var stream = fs.createReadStream('./a.json');
stream.on("error",function(err){
console.log(err)
})
複製代碼
domain模塊視圖在一個更高的維度上面解決以上提到的三種錯誤(可處理callback與event形式),可是如今這個模塊已是不推薦使用了。首先它的出發點就是把不一樣的處理方式統一到這個模塊裏面監聽和捕獲。
它的用法是使用了create方法進行建立Domain對象,而後經過Domain對象監聽某對象的error事件,且定義好了相應的處理邏輯,最後使用run方法來啓動整個Domain,run方法裏面的內容就是咱們準備監聽的代碼。
//處理callback
var fs = require("fs");
var domain = require("domain");
var d = domain.create();
d.run(function(){
fs.readFile('./a1.json','utf8',function(err,data){
if(err){
throw new Error('error')
}
console.log(data)
})
})
d.on('error',function(err){
console.log(err)
})
複製代碼
//處理event
var fs = require("fs");
var domain = require("domain");
var d = domain.create();
d.run(function(){
fs.createReadStream('./a1.json')
})
d.on('error',function(err){
console.log(err)
})
複製代碼
除此以外,domain能夠支持手動調用add方法把對象添加到監聽列表當中。因而可知,Domain其實就是將須要管理的對象包裹起來而後經過run與add方法進行處理和實現。可是若是咱們想要把整一個web服務監聽的話就是把全部代碼都放到run方法裏面,可能會形成內存泄露,並且手動調用add方法很難以接受,假如對象被遺漏就可能會花費不少時間進行錯誤排查。
它的原理其實很簡單:
ES6咱們在工做中用的比較多,好比咱們經常使用的就有promise對象了,還有async/await的形式,被稱爲是異步的終極解決方案。因此咱們也來談一下ES6中如何進行錯誤處理。
首先第一個確定是promise了,由於這對於回調函數的操做很友好,避免了一些回調地獄的產生,也提供了try/catch的形式捕獲異常。
var promise = new Promise((resolve,reject){
throw new Error("出錯啦!")
})
promise.catch(function(error){
console.log(error);
})
複製代碼
Generator與async
可使用try/catch語句進行錯誤捕獲,當yield後面的異步操做發生了錯誤,同樣可使用try語句進行捕獲。
function * generator(){
try{
yield asyncFunction();
}catch(e){
console,log(e)
}
return 'end'
}
複製代碼
假如咱們使用async的形式來寫(其實就是語法糖,本質同樣),也可使用try/catch來捕獲。假如await內部操做出錯則後續代碼不會執行,可以使用try進行包裹。
async function test(){
try{
await asyncFunction()
}catch(e){
console.log(e)
}
}
複製代碼
以上咱們介紹瞭如何在異步的世界裏面進行錯誤的捕獲,以前咱們進行的代碼編寫都是在同步的世界裏,使用try/catch就能夠解決大部分你的問題。可是近年來咱們出現了Node,同步的世界被打破了,因此咱們也有必要學習一下如何進行錯誤的捕獲。
上面咱們說了使用原始的方法try/catch、callback回調函數、事件觸發機制三種方法。
假如咱們遇到一些不可避免的錯誤,致使系統崩潰或者程序得不到正常運行,咱們仍是有最後的解決方法而且大部分是有效的,那就是:重啓試試!