前端开发系列090-Node篇之Event
一、EventEmitter介绍
Node中的event
模块实现了事件处理相关功能,在该模块中定义了EventEmitter类。
在Node中,所有可能触发事件的对象都是继承了EventEmitter类的子类的实例对象,该类定义了诸多方法,譬如添加事件监听和移除事件监听以及自动触发事件等(通过下面的打印可以发现,这些方法均定义在原型对象上面)。
wendingding$ node
> console.dir(events,{showHidden:true,colors:true,depth:1})
{ [Function: EventEmitter]
[length]: 0,
[name]: 'EventEmitter',
[prototype]:
EventEmitter {
[constructor]: [Circular],
domain: undefined,
_events: undefined,
_maxListeners: undefined,
setMaxListeners: [Object],
getMaxListeners: [Object],
emit: [Object],
addListener: [Object],
on: [Object],
prependListener: [Object],
once: [Object],
prependOnceListener: [Object],
removeListener: [Object],
removeAllListeners: [Object],
listeners: [Object],
listenerCount: [Object],
eventNames: [Object] },
EventEmitter: [Circular],
usingDomains: true,
defaultMaxListeners: [Getter/Setter],
init: { [Function] [length]: 0, [name]: '', [prototype]: [Object] },
listenerCount: { [Function] [length]: 2, [name]: '', [prototype]: [Object] } }
EventEmitter结构说明
上面的代码是在REPL环境中直接通过console.dir方法
来打印events整个模块的输出。为了方便理解,我这里另外再提供一份EventEmitter构造函数的具体实现作为对照和参考。
function EventEmitter() { EventEmitter.init.call(this); }
EventEmitter.usingDomains = false;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._eventsCount = 0; // 事件数量
EventEmitter.prototype._maxListeners = undefined; // 最大的监听器数
events
模块中的EventEmitter指向的是模块自身。
wendingding$ node
> events == events.EventEmitter
true
EventEmitter构造函数身上有个比较重要的静态方法即init方法
,该方法主要用来根据条件执行初始化操作,所谓的初始化具体点讲其实就是设置_events
、_maxListeners
,下面列出该方法的具体实现
EventEmitter.init = function() {
if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events)
{
this._events = Object.create(null);
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || undefined;
};
EventEmitter的defaultMaxListeners
属性内部实现了Getter和Setter方法,该属性用来设置和获取指定事件可以绑定的最大事件处理函数数量,该属性默认值为10。对于默认事件绑定数量,我们可以通过在REPL环境中直接打印events.defaultMaxListeners
来进行查看。此外,在EventEmitter原型对象上面,提供了setMaxListeners
和getMaxListeners
方法来对_maxListeners
属性进行获取和设置。
![](http://img.wendingding.vip/blog/node_event.png)
二、Event相关方法
前文对events
模块中的EventEmitter构造函数进行了简单介绍,接下来将主要说明具体事件监听绑定、移除和触发等相关的方法。
事件的监听(绑定)
当需要给指定对象添加事件监听(绑定事件处理函数)的时候,可以使用EventEmitter原型对象中的on方法
或者是addListener方法
,这两个方法的区别仅仅在于方法名,内部的实现一模一样,均调用events模块中的_addListener方法
来实现。
_addListener方法核心过程
>① 验证监听器是否为函数对象。
>② 避免类型为`newListener`的事件类型,造成递归调用。
>③ 优化单个监听器的场景,不需使用额外的数组对象。
>④ 基于prepend参数的值,控制监听器的添加顺序。
这里先介绍事件监听(绑定)相关方法的语法,然后给出简短示例。
EventEmitterInstance.on(event,listener)
EventEmitterInstance.addListener(event,listener)
参数说明 on
方法和addListener
方法接受两个参数,其中event代表的是事件的类型(事件的名称,譬如data
、request
等),而listener参数代表的是侦听器(事件处理函数)。
//备注:demo.js文件的内容
//001 导入模块
var http = require("http");
//002 创建server服务
var server = http.createServer();
//003 事件监听
// server.on("request",function(req,res){
server.addListener("request",function(req,res){
console.log("request--事件被触发");
console.log("req.url:",req.url);
//005 结束响应
res.end();
});
//004 开启监听
server.listen(4001,"127.0.0.1");
切换到当前所在目录,在终端中通过node demo.js
命令执行上面的代码开启服务器端口监听,然后在浏览器中输入http://127.0.0.1:4001 地址,能够得到下面的打印输出:
wendingding$ node demo.js
request--事件被触发
req.url: /
request--事件被触发
req.url: /favicon.ico
备注 在给指定对象绑定事件监听的时候,支持给某类型的事件(譬如request
)绑定多个处理函数,默认最多可以绑定10个且支持给指定对象绑定多种类型的事件监听(譬如request
事件、end
事件等)。绑定处理函数的数量可以通过getMaxListeners
和setMaxListeners
方法来操作。
var events = require("events");
//获取events模块(EventEmitter构造函数)的defaultMaxListeners属性值
console.log("defaultMaxListeners==",events.defaultMaxListeners);
console.log("默认getMaxListeners ==",rs.getMaxListeners());
rs.setMaxListeners(20)
console.log("设置getMaxListeners == 20");
console.log("最新getMaxListeners ==",rs.getMaxListeners());
//打印信息
defaultMaxListeners== 10
默认getMaxListeners == 10
设置getMaxListeners == 20
最新getMaxListeners == 20
除了上面介绍的on
方法和addListener
方法之外,once
方法也能用来给指定的对象添加事件监听(绑定事件处理函数),区别在于once
方法当事件处理函数执行一次后绑定就会立即被解除。
也就是该事件处理函数只会被执行一次。下面给出具体语法和简短示例:
EventEmitterInstance.once(event,listener)
//备注:once.js文件的内容
var fs = require("fs");
var rs = fs.createReadStream("Hi.text",{highWaterMark:10});
var data = [];
//001 监听rs实例对象的data事件,当事件触发的时候执行回调函数组织读取的Buffer数据
rs.addListener("data",function(chunk){
console.log("data...事件被触发");
data.push(chunk);
})
//002 监听rs实例对象的end事件,该事件当文件数据流读取完毕的时候触发
// 因为end事件只会被触发一次,所以完全可使用once方法来监听和绑定
rs.once("end",function(){
console.log("end····事件被触发");
var buf = Buffer.concat(data);
console.log(buf.toString());
});
//备注:执行once.js文件测试once方法的使用
wendingding$ node once.js
data...事件被触发
data...事件被触发
data...事件被触发
data...事件被触发
end····事件被触发
文顶顶:嗨,很高兴遇见你!
移除事件监听(解绑)
若需要解除指定对象的事件侦听,则可使用移除事件相关的方法。EventEmitter提供了removeListener
方法和removeAllListeners
方法来进行解绑操作,listeners
方法可以获取当前对象中指定事件绑定的所有处理函数。下面先列出语法然后给出简短代码示例:
EventEmitterInstance.listeners(event)
EventEmitterInstance.removeListener(event,listener)
EventEmitterInstance.removeAllListeners(event)
//备注:removeListener.js文件的内容
var http = require("http");
var server = http.createServer();
function requestHandleOne (req,res) {
console.log("url == ",req.url);
console.log("request1····事件被触发");
res.end();
}
function requestHandleTwo (req,res) {
console.log("url == ",req.url);
console.log("request2····事件被触发");
res.end();
}
//给server对象添加request事件监听
server.addListener("request",requestHandleOne)
server.addListener("request",requestHandleTwo)
//开启服务端口监听
server.listen(4002,"127.0.0.1");
//获取事件监听处理函数
console.log("request事件处理函数列表:");
var handleList = server.listeners("request");
for(var key in handleList)
{
console.log("事件处理函数:",handleList[key].name);
}
console.log("移除前:",server.listenerCount("request"));
//移除事件监听(解除单个事件处理函数)
server.removeListener("request",requestHandleOne);
//移除事件监听(解除指定事件的所有处理函数)
//server.removeAllListeners("request");
console.log("移除后:",server.listenerCount("request"));
var events = require("events");
//获取指定对象中指定事件侦听的数量
var server_request_listenerCount = events.EventEmitter.listenerCount(server,"request")
console.log("server对象中request事件的侦听数量:",server_request_listenerCount);
代码说明
在上面的代码中我们首先创建了服务器sever
对象,然后开启了服务器4002端口监听,并使用方法addListener
为request
事件添加两个侦听(分别是requestHandleOne
和requestHandleTwo
)函数。
server.listeners("request")
函数用来获取sever对象上所有request事件处理函数列表。
server.listenerCount("request")
函数用来获取sever对象上所有request事件处理函数的数量。
此外,在events模块的EventEmitter构造函数身上拥有一个listenerCount
方法,该方法接收两个参数(对象,事件
)以获取特定对象身上指定事件处理函数的数量。运行JavaScript代码,并在浏览器中访问:
wendingding$ node removeListener.js
request事件处理函数列表:
事件处理函数: requestHandleOne
事件处理函数: requestHandleTwo
移除前: 2
移除后: 1
server对象中request事件的侦听数量: 1
url == /
request2····事件被触发
url == /favicon.ico
request2····事件被触发