事件发生器
在Node中很多对象都能发射事件,比如客户端连接的时候,Tcp服务器会发射connect
事件等等。这些对象被称为事件发射器,而事件发生时就会调用对应的回调函数。这是典型的发布订阅模式。
还可以创建自定义事件发生器,因为Node提供了一个伪类EventEmitter
。
标准回调模式
在回调模式下,每个函数在执行完毕后可以调用另一个函数以使得程序能够继续运行下去。
举个例子:
var fs = require("fs");
fs.readFile('./test',function(err,fileContent){
if(err) {
throw err;
}
console.log(fileContent.toString());
})
可以看到,readFile
函数的第二参数是一个函数,而这个函数接受到的第一个参数是上一级函数发生错误时的错误信息,第二个参数是上一级函数的返回值。
理解事件发生器模式
在回调模式中,如果在函数执行时,发生了多个事件或者事件反复发生了多次,这时候回调模式工作的就不是很好。而这时事件发生器模式将会派上用场。
在使用事件发生器模式时,通常会设计多个对象,包括事件发生器和多个监听器。
事件发生器是可以发射事件的对象,而监听器是绑定到事件发射器上的代码,负责监听对应事件类型,来看个例子:
const req = http.request(options,function(response){
response.on("data",function(data){
console.log("data from response:" + data);
});
response.on("on",function(){
console.log("response ended");
})
});
req.end();
在上面的代码中,我利用Node的http
库创建了一个请求,当HTTP服务器响应的时候会调用一个回调函数,request
执行完毕后,就会将响应对象传递给回调函数,而这个响应对象,即上述代码中的response
就是一个事件发生器,他能够发出很多个事件,如上面代码里面有的data、end事件。每当这些事件发生的时候,后面的监听函数就会执行。
大家可以结合代码具体理解一下两种模式的不同。一般而言,需要在操作完成后重新获取控制权的时候就用回调模式,当事件可能发生多次的时候就用事件发射器模式。
事件类型
事件是具有类型的,如上面的“data”、“end”,使用字符串标明的。你无法通过编程判断事件内部会出发什么事件,所以一般这些API都是有文档的,看文档的能力也很重要。
一旦有相关事件发生,事件发射器就会调用相对的事件监听器,并将相关数据作为参数传递。如上面的data事件,会接受一个data参数,而end事件没有参数,不用接收。
虽然事件发射器是服务任意类型事件的通用接口,但是有一个特例即error
事件。大多数程序在发生错误的时候都会发射error
事件,如果没有对应的监听,那么事件发射器会向上跑出一个未捕获的异常。
事件发生器API
任何一个实现了事件发生器模式的对象(如上面用过的http)都实现了如下方法:
addLinstener
和on
:添加事件监听器。once
:给指定事件绑定一个只会调用一次的监听器。removeEventListener
:删除某个监听器。removeAllEnventListener
:删除所有监听器。
几个小点:
on
只是addLinstener
的简写,都一样。这样用:obj.on("事件名",传递的对应处理函数);
- 可以给同一个发射器发射的同类型事件绑定多个监听器,会从上到下执行每个监听器函数。这意味着,有可能事件发生之后,在调用他的监听器之前会有别的事件的监听器被执行。
removeListener
是这么用的:obj.removeListener("事件类型",监听函数名); //函数必须有名字才能删除,匿名的怎么删对不对。
- 删除所有,这个用的少且,不推荐使用,如果需要删除自己创建对象的所有监听器,也可以。传入事件名可删除对应事件上所有监听器,传入
SIGTERM
字符串可删除进程中所有监听器。
创建事件监听器
从node事件发射器继承
可以创建一个继承自EventEmitter
的伪类。
const util = require('util');
const ee = require('events').EventEmitter;
// 创建一个类构造器
const MyClass = function(){
}
// 继承,inherits的翻译就是继承
// 这个方法创建了一条原型链,使得MyClass能够使用原型上的方法。
util.inherits(MyClass,ee);
发射事件
通过继承的类,就可以发射事件了:
MyClass.prototype.someMethod = function(){
this.emit("MyEvent","argument1","argument2");
}
当someMethod
方法被调用的时候,会发射一个MyEvent
事件,后面的参数可以传递给监听器。
监听:
const instance = new MyClass();
instance.on("MyEvent",e1,e2) {
...
}
小结
- 发射器模式实现了特定事件与触发后执行逻辑的解耦。
- 使用
on
来注册监听器等等。 - 可以通过继承
EventEmitter
类以及使用emit()
函数来创建自定义事件发射器。