node.js事件循環

關鍵詞:Event Loop

Node.js 使用事件驅動模型,當web server接收到請求,就把它關閉然后進行處理,然后去服務下一個web請求。
當這個請求完成,它被放回處理隊列,當到達隊列開頭,這個結果被返回給用戶。
這個模型非常高效可擴展性非常強,因為webserver一直接受請求而不等待任何讀寫操作。(這也被稱之為非阻塞式IO或者事件驅動IO)
在事件驅動模型中,會生成一個主循環來監聽事件,當檢測到事件時觸發回調函數。

Paste_Image.png

整個事件驅動的流程就是這么實現的,非常簡潔。有點類似于觀察者模式,事件相當于一個主題(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 不會打亂對象原有的繼承關系。

文章轉載自:http://www.runoob.com/nodejs/nodejs-event.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容