事件EventEmitter可謂是NodeJS中的重中之重!
大多數(shù) Node.js 核心 API 都采用慣用的異步事件驅(qū)動(dòng)架構(gòu),其中某些類型的對(duì)象(觸發(fā)器)會(huì)周期性地觸發(fā)命名事件來調(diào)用函數(shù)對(duì)象(監(jiān)聽器)。
例如,net.Server 對(duì)象會(huì)在每次有新連接時(shí)觸發(fā)事件;fs.ReadStream 會(huì)在文件被打開時(shí)觸發(fā)事件;流對(duì)象 會(huì)在數(shù)據(jù)可讀時(shí)觸發(fā)事件。
所有能觸發(fā)事件的對(duì)象都是 EventEmitter 類的實(shí)例。 這些對(duì)象開放了一個(gè) eventEmitter.on() 函數(shù),允許將一個(gè)或多個(gè)函數(shù)綁定到會(huì)被對(duì)象觸發(fā)的命名事件上。 事件名稱通常是駝峰式的字符串,但也可以使用任何有效的 JavaScript 屬性名。
當(dāng) EventEmitter 對(duì)象觸發(fā)一個(gè)事件時(shí),所有綁定在該事件上的函數(shù)都被同步地調(diào)用。 監(jiān)聽器的返回值會(huì)被丟棄。
-
事件的基本使用
首先需要引入
/*事件*/
var EventEmitter=require("events");
require("events")返回的EventEmitter是一個(gè)類(或者說events模塊exports導(dǎo)出的是一個(gè)class)然后:
/*事件*/
var EventEmitter=require("events");
const event=new EventEmitter();//創(chuàng)建一個(gè)事件發(fā)射器實(shí)例
//注冊(cè)異步事件
event.on("customEvent",function()
{
console.log("customEvent事件觸發(fā)");
});
//觸發(fā)事件
event.emit("customEvent");
我們通過 const event=new EventEmitter()得到一個(gè)實(shí)例對(duì)象event
接著可以通過event.on(eventName,callbackfunc)來注冊(cè)一個(gè)異步事件
在需要觸發(fā)eventName事件時(shí)使用 event.emit(eventName)進(jìn)行事件的觸發(fā),事件可以注冊(cè)多個(gè)相同事件的監(jiān)聽器,在觸發(fā)該事件時(shí),依次按照注冊(cè)的順序進(jìn)行觸發(fā)事件監(jiān)聽器的回調(diào)方法callbackfunc。
-
事件監(jiān)聽器回調(diào)方法中的this
事件監(jiān)聽器回調(diào)方法中的this,指向的對(duì)象會(huì)有不同:
在function(){this}中this指向的是觸發(fā)該事件的發(fā)射器實(shí)例對(duì)象,也就是 下面示例中的 myEmitter
在箭頭函數(shù)()=>{this}中this指向的則是myEmitter所在的父作用域?qū)ο蟆?/li>
class MyEmitter extends EventEmitter{
constructor(name)
{
super();
this.name=name;
}
getName()
{
return this.name;
}
}
const myEmitter=new MyEmitter("自定義事件發(fā)射器");
myEmitter.on("clicktest",function()
{
//會(huì)打?。篶licktest事件觸發(fā) 自定義事件發(fā)射器
console.log("clicktest事件觸發(fā)",this.getName());
});
myEmitter.on("clicktest",()=>{
//箭頭函數(shù)中this指向的并不是myEmitter
//會(huì)打?。篶licktest事件觸發(fā) myEmitter父作用域中的getName()方法
//如果把下面的this.getName=function(){}注釋掉,這個(gè)事件觸發(fā)后會(huì)報(bào)錯(cuò),因?yàn)檎也坏絞etName()方法;
console.log("clicktest事件觸發(fā)",this.getName());
});
this.getName=function()
{
return "myEmitter父作用域中的getName()方法";
}
//觸發(fā)事件
myEmitter.emit("clicktest");
-
事件發(fā)生的同步與異步
EventEmitter 會(huì)按照監(jiān)聽器注冊(cè)的順序同步地調(diào)用所有監(jiān)聽器。 所以需要確保事件的正確排序且避免競(jìng)爭(zhēng)條件或邏輯錯(cuò)誤。 監(jiān)聽器函數(shù)可以使用 setImmediate() 或 process.nextTick() 方法切換到異步操作模式:
//同步與異步事件發(fā)生
myEmitter.on("asyncEvent",function()
{
setImmediate(()=>{
console.log("asyncEvent事件發(fā)生是異步的");
});
console.log("asyncEvent事件發(fā)生是同步的");
});
myEmitter.emit("asyncEvent");
console.log("看誰(shuí)先執(zhí)行,異步和同步");
執(zhí)行輸出:
asyncEvent事件發(fā)生是同步的
看誰(shuí)先執(zhí)行,異步和同步
asyncEvent事件發(fā)生是異步的
-
只觸發(fā)一次事件
我們使用 myEmitter.on()注冊(cè)的事件,每當(dāng)在調(diào)用 myEmitter.emit()時(shí)都會(huì)觸發(fā)一次監(jiān)聽器回調(diào),如果我們需要只讓他觸發(fā)一次監(jiān)聽器回調(diào),應(yīng)該使用
myEmitter.once()進(jìn)行事件的注冊(cè)
//只執(zhí)行一次監(jiān)聽回調(diào)
myEmitter.on("myevent",function()
{
console.log("myevent事件觸發(fā)");
});
myEmitter.once("myevent2",function()
{
console.log("myevent2事件觸發(fā)");
});
myEmitter.emit("myevent");
myEmitter.emit("myevent");
myEmitter.emit("myevent2");
myEmitter.emit("myevent2");
執(zhí)行輸出:
myevent事件觸發(fā)
myevent事件觸發(fā)
myevent2事件觸發(fā)
-
newListener內(nèi)置事件
myEmitter.on("newListener",function(){});
newListener事件屬于內(nèi)置事件,含義是:當(dāng) myEmitter實(shí)例對(duì)象有 新事件監(jiān)聽器注冊(cè)時(shí) 觸發(fā)該 newListener事件。
//當(dāng)有事件注冊(cè)監(jiān)聽時(shí)觸發(fā)
myEmitter.on("newListener",function()
{
console.log("newListener事件觸發(fā)");
});
myEmitter.on("myevent",function()
{
console.log("myevent事件觸發(fā)");
});
//只執(zhí)行一次監(jiān)聽回調(diào)
myEmitter.once("myevent2",function()
{
console.log("myevent2事件觸發(fā)");
});
執(zhí)行輸出
newListener事件觸發(fā)
newListener事件觸發(fā)
- removeListener事件移除
//事件移除
myEmitter.on("genEvent",handlerEvent);
function handlerEvent()
{
console.log("genEvent事件觸發(fā)");
}
myEmitter.emit("genEvent");
//移除改事件監(jiān)聽后,后續(xù)再emit該事件則忽略不會(huì)觸發(fā)監(jiān)聽回調(diào)
myEmitter.removeListener("genEvent",handlerEvent);
myEmitter.emit("genEvent");
執(zhí)行輸出:
genEvent事件觸發(fā)
最大監(jiān)聽器注冊(cè)數(shù)量
每個(gè)事件默認(rèn)可以注冊(cè)最多 10 個(gè)監(jiān)聽器。 單個(gè)EventEmitter實(shí)例的限制可以使用emitter.setMaxListeners(n)方法改變emitter.getMaxListeners()獲取最大監(jiān)聽器數(shù)
返回EventEmitter當(dāng)前的最大監(jiān)聽器限制值,該值可以通過emitter.setMaxListeners(n)
nodejs性能的提升大部分原因是得益于 異步事件機(jī)制。它摒棄掉了多線程,使用單線程進(jìn)行處理主邏輯(當(dāng)然io操作依然是多線程處理)通過異步事件來節(jié)約cpu的等待周期,最大限度的壓榨cpu資源。