Node.js所有的异步I/O操作在完成时都会发送一个事件到事件队列。
Node.js里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时触发一个事件,一个fs.readStream对象会在文件被打开的时候触发一个事件。所有这些产生事件的对象都是events.EventEmitter的实例。
EventEmitter是Node.js中用于创建、注册和触发事件的核心模块。
EventEmitter是事件驱动编程的基础,可以帮助开发者轻松实现事件的发布与订阅机制。
events模块只提供了一个对象:events.EventEmitter。
EventEmitter的核心就是事件触发与事件监听器功能的封装。
你可以通过require("events");来访问该模块。
EventEmitter对象如果在实例化时发生错误,会触发error事件。当添加新的监听器时,newListener事件会触发,当监听器被移除时,removeListener事件被触发。
下面我们用一个简单的例子说明EventEmitter的用法:
运行这段代码,1秒后控制台输出了some_event事件触发。其原理是event对象注册了事件some_event的一个监听器,然后我们通过setTimeout在1000毫秒以后向event对象发送事件some_event,此时会调用some_event的监听器。执行结果如下:
$nodeevent.jssome_event事件触发EventEmitter的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter支持若干个事件监听器。
当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。
让我们以下面的例子解释这个过程:
执行以上代码,运行的结果如下:
$nodeevent.jslistener1arg1参数arg2参数listener2arg1参数arg2参数以上例子中,emitter为事件someEvent注册了两个事件监听器,然后触发了someEvent事件。
运行结果中可以看到两个事件监听器回调函数被先后调用。这就是EventEmitter最简单的用法。
EventEmitter提供了多个属性,如on和emit。on函数用于绑定事件函数,emit属性用于触发一个事件。接下来我们来具体看下EventEmitter的属性介绍。
server.on('connection',function(stream){console.log('someoneconnected!');});3once(event,listener)为指定事件注册一个单次监听器,即监听器最多只会触发一次,触发后立刻解除该监听器。server.once('connection',function(stream){console.log('Ah,wehaveourfirstuser!');});4removeListener(event,listener)移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。
它接受两个参数,第一个是事件名称,第二个是回调函数名称。
varcallback=function(stream){console.log('someoneconnected!');};server.on('connection',callback);//...server.removeListener('connection',callback);5removeAllListeners([event])移除所有事件的所有监听器,如果指定事件,则移除指定事件的所有监听器。6setMaxListeners(n)默认情况下,EventEmitters如果你添加的监听器超过10个就会输出警告信息。setMaxListeners函数用于改变监听器的默认限制的数量。7listeners(event)返回指定事件的监听器数组。8emit(event,[arg1],[arg2],[...])按监听器的顺序执行执行每个监听器,如果事件有注册监听返回true,否则返回false。类方法序号方法&描述1listenerCount(emitter,event)返回指定事件的监听器数量。events.EventEmitter.listenerCount(emitter,eventName)//已废弃,不推荐events.emitter.listenerCount(eventName)//推荐注册一个事件监听器:
输出:
Hello,Alice!使用once()注册一次性事件:
myEmitter.once('init',()=>{console.log('Initializationeventoccurred');});myEmitter.emit('init');//打印:InitializationeventoccurredmyEmitter.emit('init');//不会再触发事件触发时可以传递参数,供监听器使用:
如果触发的事件没有对应的监听器,EventEmitter会抛出错误:
event-字符串,事件名称
listener-处理事件函数
该事件在添加新监听器时被触发。
从指定监听器数组中删除一个监听器。需要注意的是,此操作将会改变处于被删监听器之后的那些监听器的索引。
以下实例通过connection(连接)事件演示了EventEmitter类的应用。
创建main.js文件,代码如下:
以上代码,执行结果如下所示:
$nodemain.js2个监听器监听连接事件。监听器listener1执行。监听器listener2执行。listener1不再受监听。监听器listener2执行。1个监听器监听连接事件。程序执行完毕。error事件EventEmitter定义了一个特殊的事件error,它包含了错误的语义,我们在遇到异常的时候通常会触发error事件。
当error被触发时,EventEmitter规定如果没有响应的监听器,Node.js会把它当作异常,退出程序并输出错误信息。
我们一般要为会触发error事件的对象设置监听器,避免遇到错误后整个程序崩溃。例如:
varevents=require('events');varemitter=newevents.EventEmitter();emitter.emit('error');运行时会显示以下错误:
为什么要这样做呢?原因有两点:
首先,具有某个实体功能的对象实现事件符合语义,事件的监听和发生应该是一个对象的方法。
其次JavaScript的对象机制是基于原型的,支持部分多重继承,继承EventEmitter不会打乱对象原有的继承关系。
大多数Node.js核心模块(如HTTP、文件系统)都继承了EventEmitter,你可以创建自己的类来继承EventEmitter。
THMAIL
1、eventEmitter.on()与eventEmitter.addListener()没有区别,且一个事件可以绑定多个回调函数;
2、若事件队列中出现一个未绑定事件则触发error事件,若未绑定error事件则程序抛出异常结束执行
1.0版
187***17005@163.com
EventEmitter里面的error事件,EventEmitter即使绑定了error事件,也是不会输出的。而是会在控制台打印该异常的堆栈信息,并结束进程。获取异常只能通过trycatch。
eventEmitter.on('error',function(err){console.error('Error:',err);});我测试了一下,绑定error事件。只能自己触发,eventEmitter.emit('error');当没有错误时,会在控制台打印Error:undefined。有错误时,不会打印,直接打印该异常的堆栈信息,并结束进程。
tiandashu
107***5714@qq.com
1、使用类方法listenerCount获取指定事件的监听数量替代方案。
eventEmitter.listeners('connection').length2、removeListener('connection',callback);此处参数callback必须和监听器中的回调函数是同一个,否则不生效。
sakura_rain
271***2293@qq.com
如果大家学过类似的语言可以深切感受到,on和emit的真正含义。
举个例子:
//注册一个监听事件eventEmitter.on('time_delay_event',function(){console.log('time_delay_event执行',newDate().getTime());});//执行一次setTimeout(function(){eventEmitter.emit('time_delay_event');},2000);//在尝试一次setTimeout(function(){eventEmitter.emit('time_delay_event');},4000);on:注册emit:执行一次注册,不被销毁的情况下可以多次调用执行。
这也是它和once之间的区别。
Toad
gua***nlv@gmail.com
res.addListener("finish",()=>{console.log("serverresponseisfinished");});和
server.on("connection",()=>{console.log("aclienthasconnectedtotheserver!");});底层实现都是event模块来实现。可以通过查看NodeJS文档来看继承关系。