美文网首页
Nodejs EventEmitter - 源码阅读

Nodejs EventEmitter - 源码阅读

作者: ithankzc | 来源:发表于2021-09-05 00:46 被阅读0次

为什么会阅读

在 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, 自然也就不会执行事件了

文档参考

https://nodejs.org/docs/latest-v10.x/api/events.html

相关文章

网友评论

      本文标题:Nodejs EventEmitter - 源码阅读

      本文链接:https://www.haomeiwen.com/subject/kkudwltx.html