Node.js domain模塊處理錯(cuò)誤的問題操作
1、domain模塊概述
眾所周知,在JavaScript腳本代碼中,雖然可以使用try…catch機(jī)制來捕捉同步方法中拋出的錯(cuò)誤,但是不能使用try…catch機(jī)制來捕捉異步方法中拋出的錯(cuò)誤。
例如,由于在下面這段代碼中使用了同步模式的readFileSync方法讀取文件,因此可以使用try…catch機(jī)制來捕捉在執(zhí)行該方法時(shí)拋出的錯(cuò)誤。
varfs=require('fs');
try{
var data=fs.readFileSync('test.txt','utf8');
//在控制臺中輸出文件內(nèi)容
console.log(data);
}
catch(ex){
console.log('讀取文件時(shí)發(fā)生錯(cuò)誤。');
}現(xiàn)在查看如下所示的代碼。在這段代碼中,創(chuàng)建了一個(gè)HTTP服務(wù)器,該服務(wù)器在接收到客戶端請求后,向客戶端返回“你好”字符串。在用于創(chuàng)建HTTP服務(wù)器的createServer方法所使用的callback參數(shù)值回調(diào)函數(shù)中,將首先調(diào)用一個(gè)并不存在的方法noneexist,這必然會(huì)導(dǎo)致回調(diào)函數(shù)中拋出一個(gè)錯(cuò)誤,假如我們采用try…catch機(jī)制,將捕捉不到該錯(cuò)誤。
var http = require('http');
try{
http.createServer(function (req, res) {
if(req.url!=="/favicon.ico"){
noneexist();
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<head><meta charset="utf-8"/></head>');
res.end('你好\n');
}
}).listen(1337, "127.0.0.1");
}
catch(err){
console.log('接收客戶端請求時(shí)發(fā)生以下錯(cuò)誤:');
console.log(err.code);
}將這段代碼保存在應(yīng)用程序根目錄下的app.js文件中,然后在命令行窗口中運(yùn)行該文件,接著在瀏覽器的地址欄中輸入“http://localhost:1337”?,此時(shí),命令行窗口中將顯示一個(gè)錯(cuò)誤被觸發(fā),應(yīng)用程序也將由于該錯(cuò)誤的觸發(fā)而導(dǎo)致關(guān)閉。

修改上述代碼,在該示例代碼中,改為使用uncaughtException事件來捕獲任何未被處理的錯(cuò)誤。
var http = require('http');
http.createServer(function (req, res) {
if(req.url!=="/favicon.ico"){
noneexist();
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<head><meta charset="utf-8"/></head>');
res.end('你好\n');
}
}).listen(1337, "127.0.0.1");
process.on('uncaughtException', function(err) {
console.log('接收客戶端請求時(shí)發(fā)生以下錯(cuò)誤:');
console.log(err);
});瀏覽器的地址欄中輸入“http://localhost:1337”?,命令行窗口中將顯示被觸發(fā)的錯(cuò)誤。雖然該錯(cuò)誤被觸發(fā),但是應(yīng)用程序不會(huì)被強(qiáng)制關(guān)閉,如圖所示。

雖然使用uncaughtException事件可以捕捉任何未被處理的錯(cuò)誤,但這是一種“粗魯”的處理方法,有可能產(chǎn)生資源泄漏、內(nèi)存泄漏等一系列非常惡劣的影響。例如,在本例中,客戶端將永遠(yuǎn)得不到服務(wù)器端的響應(yīng)。
為了解決這一問題,從Node.js v0.8版開始,提供domain模塊。該模塊中提供一個(gè)Domain對象,當(dāng)應(yīng)用程序在任何時(shí)刻拋出錯(cuò)誤時(shí)可以通知該對象,然后由該對象來統(tǒng)一處理這些錯(cuò)誤。
使用domain模塊來處理未被處理的錯(cuò)誤:
var http = require('http');
var domain = require('domain');
http.createServer(function (req, res) {
var d = domain.create();
d.once('error', function(err) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<head><meta charset="utf-8"/></head>');
res.write('服務(wù)器端接收客戶端請求時(shí)發(fā)生以下錯(cuò)誤:');
res.end(err.message);
});
d.run(function() {
if(req.url!=="/favicon.ico"){
noneexist();
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<head><meta charset="utf-8"/></head>');
res.end('你好\n');
}
});
}).listen(1337, "127.0.0.1");命令行窗口中運(yùn)行該文件,接著在瀏覽器的地址欄中輸入http://localhost:1337,雖然服務(wù)器端觸發(fā)一個(gè)錯(cuò)誤,但是客戶端可以接收到錯(cuò)誤信息,如圖所示。

2、創(chuàng)建并使用Domain對象
首先需要使用domain模塊中的create方法創(chuàng)建一個(gè)Domain對象,該方法的使用方式如下:
let domain = domain.create();
create方法沒有任何參數(shù),該方法返回被創(chuàng)建的Domain對象。該對象是一個(gè)繼承了EventEmitter類的實(shí)例對象,當(dāng)該對象捕獲到任何錯(cuò)誤時(shí),觸發(fā)該對象的error事件??梢酝ㄟ^監(jiān)聽該對象的error事件并指定事件回調(diào)函數(shù)的方法來實(shí)現(xiàn)當(dāng)捕捉到錯(cuò)誤時(shí)的處理。該回調(diào)函數(shù)的指定方法如下所示(代碼中的domain代表一個(gè)Domain對象)?。
domain.on('error', function(err) {
//事件回調(diào)函數(shù)代碼略
});
在Domain對象的error事件回調(diào)函數(shù)中,使用一個(gè)參數(shù),其參數(shù)值為被觸發(fā)的錯(cuò)誤對象。
在domain模塊中,為Domain對象定義一個(gè)name屬性值,可以使用該屬性值來設(shè)置或獲取該Domain對象的名稱。
在Domain對象被創(chuàng)建后,需要指定該對象所監(jiān)聽的代碼,我們需要將這些代碼書寫在一個(gè)函數(shù)中,并且使用Domain對象的run方法指定Domain對象監(jiān)聽該函數(shù)中的代碼。當(dāng)這些代碼觸發(fā)任何錯(cuò)誤時(shí),將被Domain對象捕獲。Domain對象的run方法的指定方法如下所示(代碼中的domain代表一個(gè)Domain對象)?。
domain.run(fn)
在Domain對象的run方法中,使用一個(gè)參數(shù),參數(shù)值為一個(gè)函數(shù)。當(dāng)該函數(shù)中觸發(fā)任何錯(cuò)誤時(shí),將被Domain對象捕獲。
let domain = require('domain');
let rfs = require('fs');
let d = domain.create();
d.name='d1';
d.on('error', (err)=>{
console.error('%s捕獲到錯(cuò)誤!',d.name,err);
});
d.run(()=>{
process.nextTick(()=>{
setTimeout(()=>{ //模擬一個(gè)回調(diào)函數(shù)
rfs.open('non-existent file', 'r', (err, fd)=>{
if (err)
throw err;
});
}, 1000);
});
});d1捕獲到錯(cuò)誤! [Error: ENOENT: no such file or directory, open 'non-existent file'] {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: 'non-existent file',
domainThrown: true
}
3、隱式綁定與顯示綁定
3.1、隱式綁定
當(dāng)使用Domain對象的run方法指定Domain對象所要監(jiān)聽的函數(shù)時(shí),函數(shù)中使用的所有繼承了EventEmitter類的實(shí)例對象(包括各種用于讀取或?qū)懭肓鲾?shù)據(jù)的對象,以及客戶端請求對象、服務(wù)器端響應(yīng)對象等各種有可能觸發(fā)事件的對象)都隱式地綁定到Domain對象上,如果這些對象觸發(fā)錯(cuò)誤,這些錯(cuò)誤都將被Domain對象捕獲。
有時(shí),我們可能不想讓代碼中的某些對象所觸發(fā)的錯(cuò)誤被Domain對象捕獲,或者想讓這些錯(cuò)誤被其他的Domain對象捕獲。在這些場合下,應(yīng)該將這些對象顯式地綁定到Domain對象上。例如,我們有時(shí)可能想為HTTP服務(wù)器綁定一個(gè)Domain對象,而為每個(gè)客戶端請求單獨(dú)綁定一個(gè)Domain對象。
3.2、顯示綁定
在實(shí)現(xiàn)顯式綁定時(shí),需要使用Domain對象的add方法,該方法的使用方式如下所示(代碼中的domain代表一個(gè)Domain對象)?。
domain.add(emitter)
在Domain對象的add方法中,使用一個(gè)參數(shù),其參數(shù)值可以為一個(gè)需要被綁定的繼承了EventEmitter類的實(shí)例對象。如果該實(shí)例對象觸發(fā)了一個(gè)錯(cuò)誤,該錯(cuò)誤將被Domain對象所捕獲并觸發(fā)Domain對象的error事件。add方法的參數(shù)值也可以為一個(gè)通過setInterval方法或setTimeout方法返回的定時(shí)器。如果該定時(shí)器所執(zhí)行的回調(diào)函數(shù)中拋出一個(gè)錯(cuò)誤,那么該錯(cuò)誤也將被Domain對象所捕獲并觸發(fā)Domain對象的error事件。
顯示綁定domain示例:
在該示例代碼中,我們創(chuàng)建一個(gè)HTTP服務(wù)器,并指定當(dāng)服務(wù)器接收到客戶端請求時(shí),首先創(chuàng)建一個(gè)Domain對象,然后使用該對象的add方法分別將用于讀取客戶端請求數(shù)據(jù)的http.IncomingMessage對象與用于發(fā)送服務(wù)器端響應(yīng)數(shù)據(jù)的http.ServerResponse對象綁定到Domain對象上,并指定當(dāng)Domain對象捕獲到錯(cuò)誤時(shí)向客戶端返回“服務(wù)器端接收客戶端請求時(shí)發(fā)生以下錯(cuò)誤:?”字符串與被觸發(fā)的錯(cuò)誤消息。在http.IncomingMessage對象接收到客戶端發(fā)送數(shù)據(jù)而觸發(fā)的data事件回調(diào)函數(shù)中,首先使用一個(gè)不存在的方法noneexists,然后向客戶端返回“你好。?”字符串。
const http = require('http');
const domain = require('domain');
http.createServer((req, res)=>{
let reqd = domain.create();
//綁定對象
reqd.add(req);
reqd.add(res);
reqd.on('error', (err)=>{
res.writeHead(200);
res.write('服務(wù)器端接收客戶端請求時(shí)發(fā)生以下錯(cuò)誤:');
res.end(err.message);
});
res.writeHead(200);
req.on('data',()=>{
noneexists();
res.write('你好。');
res.end();
});
}).listen(1337);
接下來創(chuàng)建一個(gè)向HTTP服務(wù)器提交請求并發(fā)送數(shù)據(jù)的模塊文件,代碼如代碼清單10-6所示。在該示例代碼中,我們向在代碼清單10-5所示示例中創(chuàng)建的HTTP服務(wù)器提交請求,指定當(dāng)客戶端接收到服務(wù)器端返回?cái)?shù)據(jù)時(shí)在控制臺中輸出這些數(shù)據(jù),并向HTTP服務(wù)器發(fā)送“你好。?”字符串與“再見。?”字符串。
const http = require('http');
let options = {
hostname: 'localhost',
port: 1337,
path: '/',
method: 'POST'
};
let req = http.request(options,(res)=>{
res.setEncoding('utf8');
res.on('data', (chunk)=>{
console.log('響應(yīng)內(nèi)容: '+chunk);
});
});
req.write('你好。');
req.end('再見。');
一個(gè)命令行窗口中運(yùn)行server.js文件,在另一個(gè)命令行窗口中運(yùn)行client.js文件,該命令行窗口中顯示的結(jié)果如下:

3.3、取消顯示綁定
在使用了Domain對象的add方法繼承了EventEmitter類的實(shí)例對象與該Domain對象進(jìn)行綁定后,可以使用Domain對象的remove方法取消這些對象的綁定,該方法的使用方式如下所示。
domain.remove(emitter)
在Domain對象的remove方法中,使用一個(gè)參數(shù),其參數(shù)值為一個(gè)需要被解除綁定的繼承了EventEmitter類的實(shí)例對象。在解除綁定后,如果該實(shí)例對象觸發(fā)了一個(gè)錯(cuò)誤,該錯(cuò)誤將不再被Domain對象所捕獲。
修改createServer方法所使用的callback參數(shù)值回調(diào)函數(shù)代碼,在使用了Domain對象的add方法之后,使用Domain對象的remove方法解除http.IncomingMessage對象與Domain對象的綁定,修改后的代碼如下所示。
const http = require('http');
const domain = require('domain');
http.createServer((req, res)=>{
let reqd = domain.create();
//綁定對象
reqd.add(req);
reqd.add(res);
reqd.on('error', (err)=>{
res.writeHead(200);
res.write('服務(wù)器端接收客戶端請求時(shí)發(fā)生以下錯(cuò)誤:');
res.end(err.message);
});
res.writeHead(200);
//取消綁定
reqd.remove(req);
req.on('data',()=>{
noneexists();
res.write('你好。');
res.end();
});
}).listen(1337);
然后在一個(gè)命令行窗口中運(yùn)行server.js文件,在另一個(gè)命令行窗口中運(yùn)行client.js文件,在用于運(yùn)行server.js文件的窗口中將顯示由于運(yùn)行一個(gè)不存在的方法而導(dǎo)致一個(gè)錯(cuò)誤被觸發(fā),如圖所示。

4、綁定回調(diào)函數(shù)與攔截回調(diào)函數(shù)
4.1、綁定回調(diào)函數(shù)
可以使用Domain對象的bind方法將一個(gè)回調(diào)函數(shù)與該Domain對象進(jìn)行綁定,該方法的使用方式如下所示。
domain.bind(callback)
在Domain對象的bind方法中,使用一個(gè)參數(shù),其參數(shù)值為一個(gè)回調(diào)函數(shù),用于指定需要被綁定的回調(diào)函數(shù)。bind方法返回一個(gè)函數(shù),該函數(shù)將代替原回調(diào)函數(shù)被調(diào)用。當(dāng)該函數(shù)被調(diào)用時(shí),將內(nèi)部調(diào)用bind方法的參數(shù)值回調(diào)函數(shù),當(dāng)該回調(diào)函數(shù)中拋出任何錯(cuò)誤時(shí),Domain對象將捕獲這些錯(cuò)誤,在Domain對象的error事件回調(diào)函數(shù)中的參數(shù)值為該錯(cuò)誤對象。
查看一個(gè)bind方法的使用示例,代碼如代碼清單10-7所示。在該示例代碼中,我們首先創(chuàng)建一個(gè)Domain對象,然后使用fs模塊中的readFile方法讀取一個(gè)并不存在的文件,并使用Domain對象的bind方法將讀取文件操作結(jié)束時(shí)執(zhí)行的回調(diào)函數(shù)與Domain對象綁定到一起。在回調(diào)函數(shù)中,我們指定,當(dāng)讀取文件操作發(fā)生錯(cuò)誤時(shí),將該錯(cuò)誤拋出。在Domain對象的error事件回調(diào)函數(shù)中,我們指定,當(dāng)捕獲到錯(cuò)誤時(shí),在控制臺中輸出該錯(cuò)誤。
const fs=require('fs');
const domain = require('domain');
let d = domain.create();
fs.readFile('./test.txt', d.bind(function(err, data) {
if(err)
throw err;
else
console.log(data);
}));
d.on('error', (err)=>{
console.log('讀取文件時(shí)發(fā)生以下錯(cuò)誤:');
console.log(err);
});

4.2、攔截回調(diào)函數(shù)
可以使用Domain對象的intercept方法攔截一個(gè)回調(diào)函數(shù)中觸發(fā)的錯(cuò)誤。該方法的作用及其使用方法與Domain對象的bind方法有些相似,都是捕獲一個(gè)回調(diào)函數(shù)中所觸發(fā)的錯(cuò)誤,它們的區(qū)別在于,在使用bind方法時(shí),回調(diào)函數(shù)中必須使用throw關(guān)鍵字拋出該錯(cuò)誤,而在使用intercept方法時(shí),回調(diào)函數(shù)不需要再拋出該錯(cuò)誤,該錯(cuò)誤將直接被Domain對象攔截。
intercept方法的使用方式如下所示。
domain.intercept(callback)
在Domain對象的intercept方法中,使用一個(gè)參數(shù),其參數(shù)值為一個(gè)回調(diào)函數(shù),用于指定需要被攔截的回調(diào)函數(shù)。intercept方法返回一個(gè)函數(shù),該函數(shù)將代替原回調(diào)函數(shù)被調(diào)用。當(dāng)該函數(shù)被調(diào)用時(shí),將內(nèi)部調(diào)用bind方法的參數(shù)值回調(diào)函數(shù),當(dāng)該回調(diào)函數(shù)中拋出任何錯(cuò)誤時(shí),將被Domain對象所捕獲,在Domain對象的error事件回調(diào)函數(shù)中的參數(shù)值為該錯(cuò)誤對象。
const fs=require('fs');
const domain = require('domain');
let d = domain.create();
fs.readFile('./test.txt', d.intercept(function(err, data) {
console.log(data);
}));
d.on('error', (err)=>{
console.log('讀取文件時(shí)發(fā)生以下錯(cuò)誤:');
console.log(err);
});

5、domain堆棧的彈出與推入
在Node.js中,當(dāng)使用Domain對象的run方法、bind方法與intercept方法指定Domain對象所需監(jiān)聽的函數(shù)時(shí),將把該Domain對象推入到domain模塊所維護(hù)的一個(gè)domain堆棧中。可以通過domain中的_stack屬性值來查看domain堆棧中的內(nèi)容。
5.1、推入
在該示例代碼中,分別創(chuàng)建兩個(gè)Domain對象并分別將其命名為d1與d2,然后輸出domain堆棧中的原始內(nèi)容。接著,使用d1對象的run方法指定其所監(jiān)聽的函數(shù),并在該函數(shù)代碼中指定在控制臺中輸出運(yùn)行了d1對象的run方法后堆棧中的內(nèi)容,然后使用d2對象的run方法指定其所監(jiān)聽的函數(shù),并在該函數(shù)代碼中指定在控制臺中輸出運(yùn)行了d2對象的run方法后堆棧中的內(nèi)容。
const domain = require('domain');
let d1 = domain.create();
d1.name="d1";
let d2 = domain.create();
d2.name = "d2";
console.log('原始堆棧:');
console.log(domain._stack);
d1.run(()=>{
console.log('d1對象:');
console.log(d1);
console.log('運(yùn)行d1對象后的堆棧內(nèi)容:');
console.log(domain._stack);
});
d2.run(()=>{
console.log('d2對象:');
console.log(d2);
console.log('運(yùn)行d2對象后的堆棧內(nèi)容:');
console.log(domain._stack);
});
運(yùn)行d1對象后的堆棧內(nèi)容:
[
Domain {
_events: [Object: null prototype] {
removeListener: [Function: updateExceptionCapture],
newListener: [Function: updateExceptionCapture]
},
_eventsCount: 2,
_maxListeners: undefined,
members: [],
name: 'd1',
[Symbol(kCapture)]: false,
[Symbol(kWeak)]: WeakReference {}
}
]
d2對象:
Domain {
_events: [Object: null prototype] {
removeListener: [Function: updateExceptionCapture],
newListener: [Function: updateExceptionCapture]
},
_eventsCount: 2,
_maxListeners: undefined,
members: [],
name: 'd2',
[Symbol(kCapture)]: false,
[Symbol(kWeak)]: WeakReference {}
}
運(yùn)行d2對象后的堆棧內(nèi)容:
[
Domain {
_events: [Object: null prototype] {
removeListener: [Function: updateExceptionCapture],
newListener: [Function: updateExceptionCapture]
},
_eventsCount: 2,
_maxListeners: undefined,
members: [],
name: 'd2',
[Symbol(kCapture)]: false,
[Symbol(kWeak)]: WeakReference {}
}
]
從這個(gè)結(jié)果中可以看出,在未運(yùn)行任何Domain對象的run方法、bind方法與intercept方法時(shí),domain堆棧中的內(nèi)容為空,而在運(yùn)行一個(gè)Domain對象的run方法、bind方法與intercept方法之后,該Domain對象被推入domain堆棧,另外,在運(yùn)行另一個(gè)與堆棧中當(dāng)前Domain對象并級的Domain對象的run方法、bind方法與intercept方法之后,另外,堆棧中當(dāng)前Domain對象被彈出堆棧,后一個(gè)Domain對象被推入domain堆棧。
5.2、彈出
可以使用Domain對象的exit方法將該Domain對象從domain堆棧中彈出。該Domain對象從domain堆棧中彈出后將不能再捕獲任何錯(cuò)誤。
首先創(chuàng)建了一個(gè)Domain對象,并指定該Domain對象的error事件函數(shù),在該函數(shù)代碼中指定當(dāng)Domain對象捕獲到錯(cuò)誤時(shí)在控制臺中輸出“Domain對象捕獲到錯(cuò)誤。?”字符串。然后,輸出domain堆棧中的原始內(nèi)容。接下來,使用Domain對象的run方法指定其所監(jiān)聽的函數(shù),在該函數(shù)代碼中指定在控制臺中輸出運(yùn)行了Domain對象的run方法后堆棧中的內(nèi)容,最后拋出一個(gè)自定義錯(cuò)誤。
const domain = require('domain');
let d = domain.create();
d.on('error',(err)=>{
console.log('Domain對象捕獲到錯(cuò)誤。');
});
console.log('原始堆棧:');
console.log(domain._stack);
d.run(()=>{
console.log('運(yùn)行domain對象后的堆棧內(nèi)容:');
console.log(domain._stack);
throw new Error("error");
});
運(yùn)行domain對象后的堆棧內(nèi)容:
[
Domain {
_events: [Object: null prototype] {
removeListener: [Function: updateExceptionCapture],
newListener: [Function: updateExceptionCapture],
error: [Function (anonymous)]
},
_eventsCount: 3,
_maxListeners: undefined,
members: [],
[Symbol(kCapture)]: false,
[Symbol(kWeak)]: WeakReference {}
}
]
Domain對象捕獲到錯(cuò)誤。
在Domain對象的run方法所指定監(jiān)聽的函數(shù)中,添加使用Domain對象的exit方法,并且暫時(shí)注釋掉函數(shù)中用于拋出自定義錯(cuò)誤的代碼,修改后的代碼如下所示。
const domain = require('domain');
let d = domain.create();
d.on('error',(err)=>{
console.log('Domain對象捕獲到錯(cuò)誤。');
});
console.log('原始堆棧:');
console.log(domain._stack);
d.run(()=>{
//出棧
d.exit();
console.log('運(yùn)行domain對象后的堆棧內(nèi)容:');
console.log(domain._stack);
throw new Error("error");
});
原始堆棧:
[]
運(yùn)行domain對象后的堆棧內(nèi)容:
[]
/Users/acton_zhang/前端/vscodeworkspace/node/demo/index.js:13
throw new Error("error");
^
Error: error
at Domain.<anonymous> (/Users/acton_zhang/前端/vscodeworkspace/node/demo/index.js:13:11)
at Domain.run (domain.js:373:14)
at Object.<anonymous> (/Users/acton_zhang/前端/vscodeworkspace/node/demo/index.js:8:3)
at Module._compile (internal/modules/cjs/loader.js:1118:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1138:10)
at Module.load (internal/modules/cjs/loader.js:982:32)
at Function.Module._load (internal/modules/cjs/loader.js:875:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
at internal/main/run_main_module.js:17:47在使用了Domain對象的exit方法后,該對象將不能再捕獲任何錯(cuò)誤。
可以使用Domain對象的enter方法將一個(gè)Domain對象推入domain堆棧中并使該Domain對象變?yōu)楫?dāng)前使用的Domain對象。
在Domain對象的run方法所指定監(jiān)聽的函數(shù)中,在使用了Domain對象的exit方法之后,使用該對象的enter方法再將它推入domain堆棧,并且在控制臺中顯示使用Domain對象的enter方法后domain堆棧中的內(nèi)容。修改后的代碼如下所示。
const domain = require('domain');
let d = domain.create();
d.on('error',(err)=>{
console.log('Domain對象捕獲到錯(cuò)誤。');
});
console.log('原始堆棧:');
console.log(domain._stack);
d.run(()=>{
//出棧
d.exit();
console.log('運(yùn)行exit方法后的堆棧內(nèi)容:');
console.log(domain._stack);
//入棧
d.enter();
console.log('運(yùn)行enter方法后的堆棧內(nèi)容:');
console.log(domain._stack);
throw new Error("error");
});
運(yùn)行enter方法后的堆棧內(nèi)容:
[
Domain {
_events: [Object: null prototype] {
removeListener: [Function: updateExceptionCapture],
newListener: [Function: updateExceptionCapture],
error: [Function (anonymous)]
},
_eventsCount: 3,
_maxListeners: undefined,
members: [],
[Symbol(kCapture)]: false,
[Symbol(kWeak)]: WeakReference {}
}
]
Domain對象捕獲到錯(cuò)誤。
如果在一個(gè)Domain對象所監(jiān)聽的函數(shù)中使用另一個(gè)Domain對象的enter方法,另一個(gè)Domain對象將被推入到domain堆棧中,然后改為使用該Domain對象來捕捉第一個(gè)Domain對象所監(jiān)聽的函數(shù)中拋出的錯(cuò)誤。
接下來看一個(gè)在Domain對象中嵌套使用另一個(gè)Domain對象的示例。在該示例代碼中,創(chuàng)建兩個(gè)Domain對象,并分別將其命名為d1與d2。指定d1對象的error事件函數(shù),在該函數(shù)代碼中指定當(dāng)d1對象捕獲到錯(cuò)誤時(shí)在控制臺中輸出“d1對象捕獲到錯(cuò)誤。?”字符串,指定d2對象的error事件函數(shù),在該函數(shù)代碼中指定當(dāng)d2對象捕獲到錯(cuò)誤時(shí)在控制臺中輸出“d2對象捕獲到錯(cuò)誤。?”字符串。然后,輸出domain堆棧中的原始內(nèi)容。接下來,使用d1對象的run方法指定其所監(jiān)聽的函數(shù),在該函數(shù)代碼中使用d2對象的run方法指定其所監(jiān)聽的函數(shù),在這個(gè)嵌套的函數(shù)代碼中指定在控制臺中輸出在運(yùn)行了d2對象的run方法之后堆棧中的內(nèi)容,并且拋出一個(gè)自定義錯(cuò)誤。
const domain = require('domain');
let d1 = domain.create();
d1.name="d1";
d1.on('error',(err)=>{
console.log('d1對象捕獲到錯(cuò)誤。');
});
let d2 = domain.create();
d2.name = "d2";
d2.on('error',(err)=>{
console.log('d2對象捕獲到錯(cuò)誤。');
});
console.log('原始堆棧:');
console.log(domain._stack);
d1.run(()=>{
d2.run(()=>{
console.log('最終堆棧:');
console.log(domain._stack);
throw new Error("first");
});
});
原始堆棧:
[]
最終堆棧:
[
Domain {
_events: [Object: null prototype] {
removeListener: [Function: updateExceptionCapture],
newListener: [Function: updateExceptionCapture],
error: [Function (anonymous)]
},
_eventsCount: 3,
_maxListeners: undefined,
members: [],
name: 'd1',
[Symbol(kCapture)]: false,
[Symbol(kWeak)]: WeakReference {}
},
Domain {
_events: [Object: null prototype] {
removeListener: [Function: updateExceptionCapture],
newListener: [Function: updateExceptionCapture],
error: [Function (anonymous)]
},
_eventsCount: 3,
_maxListeners: undefined,
members: [],
name: 'd2',
[Symbol(kCapture)]: false,
[Symbol(kWeak)]: WeakReference {}
}
]
d2對象捕獲到錯(cuò)誤。
如果在d2對象的run方法中使用d1對象的enter方法,將把d1對象推入domain堆棧的最頂層,在使用了d1對象的enter方法之后的代碼中拋出的錯(cuò)誤將被d1對象捕獲,如圖所示。
d1.run(()=>{
d2.run(()=>{
//d1進(jìn)入棧頂
d1.enter();
console.log('最終堆棧:');
console.log(domain._stack);
throw new Error("first");
});
});
原始堆棧:
[]
最終堆棧:
[
Domain {
_events: [Object: null prototype] {
removeListener: [Function: updateExceptionCapture],
newListener: [Function: updateExceptionCapture],
error: [Function (anonymous)]
},
_eventsCount: 3,
_maxListeners: undefined,
members: [],
name: 'd1',
[Symbol(kCapture)]: false,
[Symbol(kWeak)]: WeakReference {}
},
Domain {
_events: [Object: null prototype] {
removeListener: [Function: updateExceptionCapture],
newListener: [Function: updateExceptionCapture],
error: [Function (anonymous)]
},
_eventsCount: 3,
_maxListeners: undefined,
members: [],
name: 'd2',
[Symbol(kCapture)]: false,
[Symbol(kWeak)]: WeakReference {}
},
Domain {
_events: [Object: null prototype] {
removeListener: [Function: updateExceptionCapture],
newListener: [Function: updateExceptionCapture],
error: [Function (anonymous)]
},
_eventsCount: 3,
_maxListeners: undefined,
members: [],
name: 'd1',
[Symbol(kCapture)]: false,
[Symbol(kWeak)]: WeakReference {}
}
]
d1對象捕獲到錯(cuò)誤。
如果在Domain對象中嵌套使用其他Domain對象,則在最外層的Domain對象的exit方法被調(diào)用后,該對象及其內(nèi)部嵌套的所有Domain對象都被彈出domain堆棧,所有被彈出的Domain對象都不能再捕獲任何錯(cuò)誤。
d1.run(()=>{
d2.run(()=>{
//d1出棧
d1.exit();
console.log('最終堆棧:');
console.log(domain._stack);
// throw new error("first");
});
});
原始堆棧: [] 最終堆棧: []
在使用了d1對象的exit方法后domain堆棧中的Domain對象全部彈出。
6、domain對象的銷毀
在一個(gè)Domain對象不再需要使用之后,可以使用該對象的dispose方法將其銷毀。在該對象被銷毀后,所有與該對象綁定的對象或回調(diào)函數(shù)都被解除綁定,這些對象或回調(diào)函數(shù)所觸發(fā)的錯(cuò)誤不再被該Domain對象捕獲,在Domain對象的run方法中指定的函數(shù)將不再被運(yùn)行,與Domain對象綁定的定時(shí)器也將不再被運(yùn)行。
d.dispose();
到此這篇關(guān)于Node.js domain模塊處理錯(cuò)誤的問題小結(jié)的文章就介紹到這了,更多相關(guān)Node.js domain模塊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
用npm-run實(shí)現(xiàn)自動(dòng)化任務(wù)的方法示例
這篇文章主要介紹了用npm-run實(shí)現(xiàn)自動(dòng)化任務(wù)的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-01-01
詳解用node.js實(shí)現(xiàn)簡單的反向代理
本篇文章主要介紹了詳解用node.js實(shí)現(xiàn)簡單的反向代理,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06
Node.js基礎(chǔ)入門之path模塊,url模塊,http模塊使用詳解
這篇文章主要為大家介紹了Node.js中的三個(gè)模塊(path、url、http)的使用詳解,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-03-03
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

