關鍵詞:Event Loop
Node.js 使用事件驅動模型,當web server接收到請求,就把它關閉然后進行處理,然后去服務下一個web請求。
當這個請求完成,它被放回處理隊列,當到達隊列開頭,這個結果被返回給用戶。
這個模型非常高效可擴展性非常強,因為webserver一直接受請求而不等待任何讀寫操作。(這也被稱之為非阻塞式IO或者事件驅動IO)
在事件驅動模型中,會生成一個主循環來監聽事件,當檢測到事件時觸發回調函數。
整個事件驅動的流程就是這么實現的,非常簡潔。有點類似于觀察者模式,事件相當于一個主題(Subject),而所有注冊到這個事件上的處理函數相當于觀察者(Observer)。
event模塊使用步驟
// 引入 events 模塊
var events = require('events');
// 創建 eventEmitter 對象
var eventEmitter = new events.EventEmitter();
// 綁定事件及事件的處理程序
eventEmitter.on('eventName', eventHandler);
// 觸發事件
eventEmitter.emit('eventName');
在main.js中創建一個實例
// 引入 events 模塊
var events = require('events');
// 創建 eventEmitter 對象
var eventEmitter = new events.EventEmitter();
// 創建事件處理程序
var connectHandler = function connected() {
console.log('連接成功。');
// 觸發 data_received 事件
eventEmitter.emit('data_received');
}
// 綁定 connection 事件處理程序
eventEmitter.on('connection', connectHandler);
// 使用匿名函數綁定 data_received 事件
eventEmitter.on('data_received', function(){
console.log('數據接收成功。');
});
// 觸發 connection 事件
eventEmitter.emit('connection');
console.log("程序執行完畢。");
在命令行中輸入
* $ node main.js
得到結果
連接成功。
數據接收成功。
程序執行完畢。
node應用程序的工作
var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
if (err){
console.log(err.stack);
return;
}
console.log(data.toString());
});
console.log("程序執行完畢");
- Node.js 所有的異步 I/O 操作在完成時都會發送一個事件到事件隊列。
- Node.js里面的許多對象都會分發事件:
- 一個net.Server對象會在每次有新連接時分發一個事件,
- 一個fs.readStream對象會在文件被打開的時候發出一個事件。
- 所有這些產生事件的對象都是 events.EventEmitter 的實例。
events 模塊只提供了一個對象: events.EventEmitter。EventEmitter 的核心就是事件觸發與事件監聽器功能的封裝。
你可以通過require("events");來訪問該模塊。
// 引入 events 模塊
var events = require('events');
// 創建 eventEmitter 對象
var eventEmitter = new events.EventEmitter();
EventEmitter 對象如果在實例化時發生錯誤,會觸發 'error' 事件。當添加新的監聽器時,'newListener' 事件會觸發,當監聽器被移除時,'removeListener' 事件被觸發。
下面我們用一個簡單的例子說明 EventEmitter 的用法:
//event.js 文件
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();
event.on('some_event', function() {
console.log('some_event 事件觸發');
});
setTimeout(function() {
event.emit('some_event');
}, 1000);
執行結果如下:
運行這段代碼,1 秒后控制臺輸出了 'some_event 事件觸發'。其原理是 event 對象注冊了事件 some_event 的一個監聽器,然后我們通過 setTimeout 在 1000 毫秒以后向 event 對象發送事件 some_event,此時會調用some_event 的監聽器。
EventEmitter 的每個事件由一個事件名和若干個參數組成,
事件名是一個字符串,通常表達一定的語義。
對于每個事件,EventEmitter 支持 若干個事件監聽器。
當事件觸發時,注冊到這個事件的事件監聽器被依次調用,事件參數作為回調函數參數傳遞。
讓我們以下面的例子解釋這個過程:
//event.js 文件
var events = require('events');
var emitter = new events.EventEmitter();
emitter.on('someEvent', function(arg1, arg2) {
console.log('listener1', arg1, arg2);
});
emitter.on('someEvent', function(arg1, arg2) {
console.log('listener2', arg1, arg2);
});
emitter.emit('someEvent', 'arg1 參數', 'arg2 參數');
// listener1 arg1 參數 arg2 參數
// listener2 arg1 參數 arg2 參數
以下實例通過 connection(連接)事件演示了 EventEmitter 類的應用。
創建 main.js 文件,代碼如下:
var events = require('events');
var eventEmitter = new events.EventEmitter();
// 監聽器 #1
var listener1 = function listener1() {
console.log('監聽器 listener1 執行。');
}
// 監聽器 #2
var listener2 = function listener2() {
console.log('監聽器 listener2 執行。');
}
// 綁定 connection 事件,處理函數為 listener1
eventEmitter.addListener('connection', listener1);
// 綁定 connection 事件,處理函數為 listener2
eventEmitter.on('connection', listener2);
var eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " 個監聽器監聽連接事件。");
// 處理 connection 事件
eventEmitter.emit('connection');
// 移除監綁定的 listener1 函數
eventEmitter.removeListener('connection', listener1);
console.log("listener1 不再受監聽。");
// 觸發連接事件
eventEmitter.emit('connection');
eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " 個監聽器監聽連接事件。");
console.log("程序執行完畢。");
執行結果
$ node main.js
2 個監聽器監聽連接事件。
監聽器 listener1 執行。
監聽器 listener2 執行。
listener1 不再受監聽。
監聽器 listener2 執行。
1 個監聽器監聽連接事件。
程序執行完畢。
大多數時候我們不會直接使用 EventEmitter,而是在對象中繼承它。包括 fs、net、 http 在內的,只要是支持事件響應的核心模塊都是 EventEmitter 的子類。
為什么要這樣做呢?原因有兩點:
- 首先,具有某個實體功能的對象實現事件符合語義, 事件的監聽和發射應該是一個對象的方法。
- 其次 JavaScript 的對象機制是基于原型的,支持 部分多重繼承,繼承 EventEmitter 不會打亂對象原有的繼承關系。