事件驅動程序
Node.js 使用事件驅動模型,當web server接收到請求,就把它關閉然后進行處理,然后去服務下一個web請求。
當這個請求完成,它被放回處理隊列,當到達隊列開頭,這個結果被返回給用戶。
這個模型非常高效可擴展性非常強,因為 webserver 一直接受請求而不等待任何讀寫操作。(這也稱之為非阻塞式IO或者事件驅動IO)
在事件驅動模型中,會生成一個主循環來監聽事件,當檢測到事件時觸發回調函數。
?
整個事件驅動的流程就是這么實現的,非常簡潔。有點類似于觀察者模式,事件相當于一個主題(Subject),而所有注冊到這個事件上的處理函數相當于觀察者(Observer)。
?EventEmitter觸發器
大多數 Node.js 核心 API 構建于慣用的異步事件驅動架構,其中某些類型的對象(又稱觸發器,Emitter)會觸發命名事件來調用函數(又稱監聽器,Listener)。
例如??net.Server?會在每次有新連接時觸發事件 fs.ReadStream會在打開文件時觸發事件,stream會在數據可讀時觸發事件。
所有能觸發事件的對象都是?EventEmitter
?類的實例。 這些對象有一個?eventEmitter.on()
?函數,用于將一個或多個函數綁定到命名事件上。。
當?EventEmitter
?對象觸發一個事件時,所有綁定在該事件上的函數都會被同步地調用。 被調用的監聽器返回的任何值都將會被忽略并丟棄。
一個簡單的?EventEmitter
?實例,綁定了一個監聽器。?eventEmitter.on()
?用于注冊監聽器,?eventEmitter.emit()
?用于觸發事件。
Node.js 有多個內置的事件,我們可以通過引入 events 模塊,并通過實例化 EventEmitter 類來綁定和監聽事件,如下實例:
const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.on('event', () => {console.log('觸發事件');
});
myEmitter.emit('event');
?
將參數和 this 傳給監聽器??
eventEmitter.emit()
?方法可以傳任意數量的參數到監聽器函數。 當監聽器函數被調用時,?this
?關鍵詞會被指向監聽器所綁定的?EventEmitter
?實例。
const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.on('event', function(a, b) {console.log(a, b, this, this === myEmitter);
});
myEmitter.emit('event', 'a', 'b');
也可以使用 ES6 的箭頭函數作為監聽器。但?this
?關鍵詞不會指向?EventEmitter
?實例:?
const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.on('event', (a, b) => {console.log(a, b, this, this ,this===myEmitter);
});
myEmitter.emit('event', 'a', 'b');
結果輸出:
a b {} {} false?
異步 VS 同步
EventEmitter
?以注冊的順序同步地調用所有監聽器。 這樣可以確保事件的正確排序,并有助于避免競態條件和邏輯錯誤。 當適當時,監聽器函數可以使用?setImmediate()
?和?process.nextTick()
?方法切換到異步的操作模式:
const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.on('event', (a, b) => {setImmediate(() => {console.log('異步地發生');});
});
myEmitter.emit('event', 'a', 'b');
僅處理事件一次
當使用?eventEmitter.on()
?注冊監聽器時,監聽器會在每次觸發命名事件時被調用。
const events = require('events');const myEmitter = new events.EventEmitter();
let m = 0;
myEmitter.on('event', () => {console.log(++m);
});
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
// 打印: 2
使用?eventEmitter.once()
?可以注冊最多可調用一次的監聽器。 當事件被觸發時,監聽器會被注銷,然后再調用。
const events = require('events');const myEmitter = new events.EventEmitter();
let m = 0;
myEmitter.once('event', () => {console.log(++m);
});
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
// 不觸發
錯誤事件
當?EventEmitter
?實例出錯時,應該觸發?'error'
?事件。 這些在 Node.js 中被視為特殊情況。
如果沒有為?'error'
?事件注冊監聽器,則當?'error'
?事件觸發時,會拋出錯誤、打印堆棧跟蹤、并退出 Node.js 進程。
const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.emit('error', new Error('錯誤信息'));
// 拋出錯誤并使 Node.js 崩潰。
為了防止崩潰 Node.js 進程,通過使用符號?errorMonitor
?安裝監聽器,可以監視?'error'
?事件但不消耗觸發的錯誤。
const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.on(EventEmitter.errorMonitor, (err) => {MyMonitoringTool.log(err);
});
myEmitter.emit('error', new Error('錯誤'));
// 仍然拋出錯誤并使 Node.js 崩潰。