Node events文档地址:http://nodejs.cn/api/events.html
1 背景
今天使用AWS IOT时发现一个问题,当server作为节点接入AWS IOT网络对设备发送命令,发现等待响应流程有点问题。
(1)所有接受消息的行为都是在一个回调函数中执行的,意味着来自不同设备的响应都只会触发这个回调。
(2)在同级文件中定义了一个发送命令的函数,该函数要发送命令并等待设备响应再返回结果。
这两个行为就导致了一个问题,当调用发送函数时,发送函数push出消息,这里就需要等待设备发送响应,但接收函数并不在该发送函数内部,正常流程就直接return了。这里就需要一种方式让发送函数等待并接受到响应后再return。
这里发现node有一个非常方便的方案(event)解决这个问题。
2 events
大多数 Node.js 核心 API 都采用惯用的异步事件驱动架构,其中某些类型的对象(触发器)会周期性地触发命名事件来调用函数对象(监听器)。
所有能触发事件的对象都是 EventEmitter 类的实例。 这些对象开放了一个 eventEmitter.on() 函数,允许将一个或多个函数绑定到会被对象触发的命名事件上。 事件名称通常是驼峰式的字符串,但也可以使用任何有效的 JavaScript 属性名。
结合code一些概念进行说明
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('触发了一个事件!');
});
myEmitter.emit('event');
上诉代码:一个绑定了一个监听器的 EventEmitter 实例。 eventEmitter.on() 方法用于注册监听器,eventEmitter.emit() 方法用于触发事件。
下面是我对几个概念的理解。
事件:Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。
触发器:用于触发命名事件。
监听器:将命名事件与特定函数对象绑定到一起,当事件被触发时就会调用该函数,一直等待事件触发(监听)。
3 sample
3.1 Event 事件触发效果
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
let Say = function(callback){
console.info("I'll say something when I'm ready")
myEmitter.on('message', (data) => {//注册了一个监听器并绑定到message这个事件上
console.log(data);
return callback(null, "yeah,I've said it.");
});
}
Say(function(err, data){//调用say函数,say函数执行,等待message事件触发
console.info(data)
});
setTimeout(function(){
console.info("I'm ready");
myEmitter.emit('message', 'helloworld');//触发message事件,say函数监听内容继续执行
},3000);
3.2 只触发一次
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
let m = 0;
myEmitter.once('event', () => {
console.log(++m);
});
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
使用 eventEmitter.once() 方法时可以注册一个对于特定事件最多被调用一次的监听器。 当事件被触发时,监听器会被注销。
即该监听器只会触发一次。
3.3 eventEmitter的异常处理
当 EventEmitter 实例中发生错误时,会触发一个 ‘error’ 事件。 这在 Node.js 中是特殊情况。
如果没有注册error事件,则node 会直接挂掉。所以我们应该为EventEmitter 注册error事件。
const myEmitter = new MyEmitter();
myEmitter.on('error', (err) => {
console.error('有错误');
});
myEmitter.emit('error', new Error('whoops!'));
3.4 EventEmitter 类
EventEmitter 类由 events 模块定义和开放的:
const EventEmitter = require(‘events’);
当新的监听器被添加时,所有的 EventEmitter 会触发 ‘newListener’ 事件;当移除已存在的监听器时,则触发 ‘removeListener’。
3.4.1 newListener事件
当绑定新触发器时触发newListener事件
const myEmitter = new MyEmitter();
// 只处理一次,所以不会无限循环
myEmitter.once('newListener', (event, listener) => {
if (event === 'event') {
// 在开头插入一个新的监听器
myEmitter.on('event', () => {
console.log('B');
});
}
});
myEmitter.on('event', () => {
console.log('A');
});
myEmitter.emit('event');
3.4.2 removeListener事件
‘removeListener’ 事件在 listener 被移除后触发。
3.5 取消监听器
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
let m = 0;
let eventFunction = function(){
console.log(++m);
}
myEmitter.on('event',eventFunction);
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
// 打印: 2
myEmitter.removeListener('event', eventFunction);
myEmitter.emit('event');
//取消监听器 不打印
3.6 最大监听器限制
每个事件node监听器被默认限制为10。注意是每个事件 ,不同事件时可以超过10的。
emitter.getMaxListeners()获取当前最大监听器限制
emitter.setMaxListeners(n)设定当前最大监听器限制
默认情况下,如果为特定事件添加了超过 10 个监听器,则 EventEmitter 会打印一个警告。 此限制有助于寻找内存泄露。 但是,并不是所有的事件都要被限为 10 个。 emitter.setMaxListeners() 方法允许修改指定的 EventEmitter 实例的限制。 值设为 Infinity(或 0)表明不限制监听器的数量。
4 用于解决上述背景中提出的问题的code
device.on("message", function(topic, payload){//接收来自设备的消息
myEmitter.emit(topic, payload);//接受到 对应ID 的消息就触发发送函数中相应的函数返回结果
})
var sendMessage = function(topic,data, callback){
device.push(topic, data);
myEmitter.once(topic, (payload) => {
return callback(null, payload);
});
}