淺析Node是如何進行錯誤處理的

node是對錯誤處理要求比較高的語言,假如對錯誤處理沒有到位可能會造成程序進程退出

在這裏插入圖片描述

01 前言


錯誤處理是程序中一個重要的部分,也是判斷你的程序是否專業的標準。一般來說我們寫程序的時候都會選擇使用try…catch來進行錯誤捕獲,或者有時候我們會使用throw進行錯誤拋出,這是都是常用的錯誤捕獲方法。但是我們在進行node進行開發的時候就會接觸到異步過程的中的錯誤處理。

我們知道在node開發的時候會運用到很多第三方的模塊,比如我們經常會使用最大的包管理工具npm,裏面下載的包都會放到我們項目當中的node_modules裏面,我們打開可以看到裏面包含的文件很多,代碼量也是巨大的。這裏面就會有很多的bug隱患在裏面,這時候使用錯誤捕獲就非常有用了。

其實我們一開始想到的就是在全局範圍內進行錯誤的監聽,node提供了一個uncaughtException捕獲異常,但是這種方法我們會難以定位到錯誤的發生位置。不應該把該函數當成萬能的捕獲模塊,而是最後的解決方案。
在這裏插入圖片描述

02 Error模塊


Error定義了Node中常見的錯誤類型,我們可以使用Error進行錯誤的拋出。Error模塊裏面包含了一個堆棧軌跡用於描述Error是從哪裏產生的,一般來說我們可以準確知道錯誤發生在哪一部分的代碼當中,根據錯誤的描述信息可以快速定位到錯誤。

var fs = require("fs");
fs.readFile("file",function(err,data){
	if(err){
		throw new Error("Error!")
	}
})

Node程序中產生的所有Error都是使用Error類的實例或者繼承自Error類。我們在程序代碼不中不僅可以使用回調函數自帶的Error模塊,而且我們可以顯示第捕獲錯誤。比如當你知道邏輯代碼運行都某一部分是不對的,應該進行錯誤的捕獲和提醒,你就可以使用:

throw new Error("自定義錯誤信息!")

03 錯誤捕獲方式


接下來就簡單介紹一下Node中我們是如何進行錯誤捕獲的,總的來說我們可以有以下三種方式,try/catch、callback、event。之前我們常用的try/catch方式只適用於同步的調用情況,但是我們知道node中會出現很多的異步調用方式。

try/catch

首先我們應該瞭解的是在異步操作當中該方法是無法捕獲錯誤的,主要原因就是因爲異步調用返回時,代碼的上下文已經改變,回調函數當中的代碼已經脫離了try/catch的範圍,所以是無法捕獲的。

同步調用情況:

//這裏可以捕獲
try{
	throw new Error("這裏出錯了!");
}catch(e){
	console.log(e)
}

異步調用情況:

try{
	setTimeout(function(){throw new Error('這裏出錯了!')}1000)
}catch(e){
	console.log(e);//這裏無法進行捕獲
}
callback

回調函數的方式主要是通過參數的判斷來確定的,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' }
Event錯誤處理

我們進行對文件流監聽的時候,即使文件流讀取是一個同步的方法,但是我們依舊不能使用try/catch來捕獲,爲什麼呢?因爲該方法返回了一個對象,只能使用事件處理的方式來處理異常。如果使用try/catch的話直接報錯退出,使用事件監聽的方式就不會影響程序的運行且會報出錯誤信息。

所以正確的方式應該是這樣的:

var fs = require("fs");
var stream = fs.createReadStream('./a.json');
stream.on("error",function(err){
	console.log(err)
})

04 Domain模塊


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方法很難以接受,假如對象被遺漏就可能會花費很多時間進行錯誤排查。

它的原理其實很簡單:

  • 通過add方法將對象添加到其監聽列表當中(有一個menber屬性,維護監聽的對象)
  • 監聽process.uncaughtException捕獲,假如代碼被domain包裹就會觸發domain的error事件
  • 通過將異步的實現方式,加入domain進行監聽,這樣就可以把所有的不同的錯誤處理機制統一到一個對象

05 ES6中的錯誤處理


ES6我們在工作中用的比較多,比如我們常用的就有promise對象了,還有async/await的形式,被稱爲是異步的終極解決方案。所以我們也來談一下ES6中如何進行錯誤處理。

Promise

首先第一個肯定是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)
	}
}

06 小結

以上我們介紹瞭如何在異步的世界裏面進行錯誤的捕獲,之前我們進行的代碼編寫都是在同步的世界裏,使用try/catch就可以解決大部分你的問題。但是近年來我們出現了Node,同步的世界被打破了,所以我們也有必要學習一下如何進行錯誤的捕獲。

上面我們說了使用原始的方法try/catch、callback回調函數、事件觸發機制三種方法。

假如我們遇到一些不可避免的錯誤,導致系統崩潰或者程序得不到正常運行,我們還是有最後的解決方法並且大部分是有效的,那就是:重啓試試!

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章