事件EventEmitter可谓是NodeJS中的重中之重!
大多数 Node.js 核心 API 都采用惯用的异步事件驱动架构,其中某些类型的对象(触发器)会周期性地触发命名事件来调用函数对象(监听器)。
例如,net.Server
对象会在每次有新连接时触发事件;fs.ReadStream
会在文件被打开时触发事件;流对象 会在数据可读时触发事件。
所有能触发事件的对象都是 EventEmitter
类的实例。 这些对象开放了一个 eventEmitter.on()
函数,允许将一个或多个函数绑定到会被对象触发的命名事件上。 事件名称通常是驼峰式的字符串,但也可以使用任何有效的 JavaScript 属性名。
当 EventEmitter
对象触发一个事件时,所有绑定在该事件上的函数都被同步地调用。 监听器的返回值会被丢弃。
-
事件的基本使用
首先需要引入
/*事件*/
var EventEmitter=require("events");
require("events")返回的EventEmitter是一个类(或者说events模块exports导出的是一个class)然后:
/*事件*/
var EventEmitter=require("events");
const event=new EventEmitter();//创建一个事件发射器实例
//注册异步事件
event.on("customEvent",function()
{
console.log("customEvent事件触发");
});
//触发事件
event.emit("customEvent");
我们通过 const event=new EventEmitter()得到一个实例对象event
接着可以通过event.on(eventName,callbackfunc)来注册一个异步事件
在需要触发eventName事件时使用 event.emit(eventName)进行事件的触发,事件可以注册多个相同事件的监听器,在触发该事件时,依次按照注册的顺序进行触发事件监听器的回调方法callbackfunc。
-
事件监听器回调方法中的this
事件监听器回调方法中的this,指向的对象会有不同:
在function(){this}中this指向的是触发该事件的发射器实例对象,也就是 下面示例中的 myEmitter
在箭头函数()=>{this}中this指向的则是myEmitter所在的父作用域对象。
class MyEmitter extends EventEmitter{
constructor(name)
{
super();
this.name=name;
}
getName()
{
return this.name;
}
}
const myEmitter=new MyEmitter("自定义事件发射器");
myEmitter.on("clicktest",function()
{
//会打印:clicktest事件触发 自定义事件发射器
console.log("clicktest事件触发",this.getName());
});
myEmitter.on("clicktest",()=>{
//箭头函数中this指向的并不是myEmitter
//会打印:clicktest事件触发 myEmitter父作用域中的getName()方法
//如果把下面的this.getName=function(){}注释掉,这个事件触发后会报错,因为找不到getName()方法;
console.log("clicktest事件触发",this.getName());
});
this.getName=function()
{
return "myEmitter父作用域中的getName()方法";
}
//触发事件
myEmitter.emit("clicktest");
-
事件发生的同步与异步
EventEmitter 会按照监听器注册的顺序同步地调用所有监听器。 所以需要确保事件的正确排序且避免竞争条件或逻辑错误。 监听器函数可以使用 setImmediate() 或 process.nextTick() 方法切换到异步操作模式:
//同步与异步事件发生
myEmitter.on("asyncEvent",function()
{
setImmediate(()=>{
console.log("asyncEvent事件发生是异步的");
});
console.log("asyncEvent事件发生是同步的");
});
myEmitter.emit("asyncEvent");
console.log("看谁先执行,异步和同步");
执行输出:
asyncEvent事件发生是同步的
看谁先执行,异步和同步
asyncEvent事件发生是异步的
-
只触发一次事件
我们使用 myEmitter.on()注册的事件,每当在调用 myEmitter.emit()时都会触发一次监听器回调,如果我们需要只让他触发一次监听器回调,应该使用
myEmitter.once()进行事件的注册
//只执行一次监听回调
myEmitter.on("myevent",function()
{
console.log("myevent事件触发");
});
myEmitter.once("myevent2",function()
{
console.log("myevent2事件触发");
});
myEmitter.emit("myevent");
myEmitter.emit("myevent");
myEmitter.emit("myevent2");
myEmitter.emit("myevent2");
执行输出:
myevent事件触发
myevent事件触发
myevent2事件触发
-
newListener内置事件
myEmitter.on("newListener",function(){});
newListener事件属于内置事件,含义是:当 myEmitter实例对象有 新事件监听器注册时 触发该 newListener事件。
//当有事件注册监听时触发
myEmitter.on("newListener",function()
{
console.log("newListener事件触发");
});
myEmitter.on("myevent",function()
{
console.log("myevent事件触发");
});
//只执行一次监听回调
myEmitter.once("myevent2",function()
{
console.log("myevent2事件触发");
});
执行输出
newListener事件触发
newListener事件触发
- removeListener事件移除
//事件移除
myEmitter.on("genEvent",handlerEvent);
function handlerEvent()
{
console.log("genEvent事件触发");
}
myEmitter.emit("genEvent");
//移除改事件监听后,后续再emit该事件则忽略不会触发监听回调
myEmitter.removeListener("genEvent",handlerEvent);
myEmitter.emit("genEvent");
执行输出:
genEvent事件触发
最大监听器注册数量
每个事件默认可以注册最多 10 个监听器。 单个EventEmitter
实例的限制可以使用emitter.setMaxListeners(n)
方法改变emitter.getMaxListeners()获取最大监听器数
返回EventEmitter
当前的最大监听器限制值,该值可以通过emitter.setMaxListeners(n)
nodejs性能的提升大部分原因是得益于 异步事件机制。它摒弃掉了多线程,使用单线程进行处理主逻辑(当然io操作依然是多线程处理)通过异步事件来节约cpu的等待周期,最大限度的压榨cpu资源。