为什么会阅读
在 Nodejs,基本都和事件相关, 比如 net 模块中, Server 类就继承了 EventEmitter,是一个比较重要的模块了。
Node 版本
10.24.1
主要api
- on
- once
- addListener
...
增加事件监听器
new EventEmitter().on(eventName, callback)
其实等同于 new EventEmitter().addListener(eventName, callback)
看源码其实可以发现 addListener
赋值给了 on
// event.js
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
关于事件的回调函数存储
回调函数的存储是引用!!!!引用的!!!
先给大家看一个例子
myEmitter.once('data', (data) => {
console.log('d1');
})
myEmitter.once('data', (data) => {
console.log('d2');
})
myEmitter.removeListener('data', () => {
console.log('d2');
})
myEmitter.emit('data', {age:1});
// 输出结果是
d1
d2
预期是打印d1, 结果打印了d1 d2。
但认真一想,移除的回调和注册上去的回调函数是同一个吗,两个匿名函数,存储在栈的时候已经是两个指针了。
那要怎样做呢,可以将函数赋值到一个变量,然后增加和移除监听器的时候,引用同一个变量就可以啦。 调整后的代码如下
const EventEmitter = require("events");
class MyEmitter extends EventEmitter {
}
const myEmitter = new MyEmitter();
myEmitter.once('data', (data) => {
console.log('d1');
});
const listener2 = () => {
console.log('d2');
};
myEmitter.once('data', listener2);
myEmitter.removeListener('data', listener2);
myEmitter.emit('data', {age:1});
// 输出结果
d1
如果还是理解不了的话,可以看这里的这一段代码
// events.js 234 行
if (existing === undefined) {
// Optimize the case of one listener. Don't need the extra array object.
events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === 'function') {
// Adding the second element, need to change to array.
existing = events[type] =
prepend ? [listener, existing] : [existing, listener];
// If we've already got an array, just append.
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
可以看到, listener 其实都是作为指针存储到事件的对象,当 事件不存在时,注册的事件函数则直接存储到为{'data': func}
,
当事件已存在但多次监听的情况下,则存储为 {'data': [func, func1]}
,
当删除的时候, 同样也是将指针删除
//events.js 328 行
if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
else {
delete events[type];
if (events.removeListener)
this.emit('removeListener', type, list.listener || listener);
}
} else if (typeof list !== 'function') {
position = -1;
for (i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || list[i].listener === listener) {
originalListener = list[i].listener;
position = i;
break;
}
}
if (position < 0)
return this;
if (position === 0)
list.shift();
else {
if (spliceOne === undefined)
spliceOne = require('internal/util').spliceOne;
spliceOne(list, position);
}
if (list.length === 1)
events[type] = list[0];
if (events.removeListener !== undefined)
this.emit('removeListener', type, originalListener || listener);
}
once 理解
即使是 once
也是可以针对事件增加多个监听器,once
真正的意思是即使 emit
多次,也只会触发1次
那怎样做到做到触发一次的呢,我们可以看到这里
// event.js 282 行开始
function onceWrapper(...args) {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
return Reflect.apply(this.listener, this.target, args);
}
}
当 emit 一次后,就将事件移除,并标志 被炒
了,
第二次 emit 的时候,!this.fired
此时值为 false, 自然也就不会执行事件了
网友评论