node EventEmitter源码分析与实现

源码分析

  • 环境:Node v14.19.1

调试代码

const event = require('events');

const eventEmitter = new event();

eventEmitter.on('event', () => {
  console.log('ai yo.');
});

eventEmitter.on('event', () => {
  console.log('yo.');
});

eventEmitter.emit('event');

咱们就按照这个进行分析源码如何实现的

events.js#EventEmitter.init()

该方法实例化事件时执行,说白了new的时候会调用它进行初始化

EventEmitter.init = function(opts) {

  // 判断_events是否不存在,不存在即创建空对象
  if (this._events === undefined ||
      this._events === ObjectGetPrototypeOf(this)._events) {
    // 这里有点awesome,这里创建的不是普通的{}字面量对象,普通的字面量的原型链是指向Object.prototype的,这里的对象没有任何原型链,真就存粹的{}对象,这里的做法是减少原型链查找提升效率
    // 不信看下面的图纯对象
    this._events = ObjectCreate(null);
    this._eventsCount = 0;
  }

  this._maxListeners = this._maxListeners || undefined;

  // 这里是初始化穿参相关
  if (opts?.captureRejections) {
    if (typeof opts.captureRejections !== 'boolean') {
      throw new ERR_INVALID_ARG_TYPE('options.captureRejections',
                                     'boolean', opts.captureRejections);
    }
    this[kCapture] = Boolean(opts.captureRejections);
  } else {
    // Assigning the kCapture property directly saves an expensive
    // prototype lookup in a very sensitive hot path.``
    this[kCapture] = EventEmitter.prototype[kCapture];
  }
};
  • 纯对象

    image-20220618201352644

    这里的方法跟Object.create(null)一致,创建了一个不带原型链的对象

    image-20220618201640960

event.js#_addListener()

订阅事件,多个、单个不同的处理

// target: 咱刚才创建的EventEmitter类
// type: 订阅的事件名,咱们代码里面名为'event'的字符串
// listener: 订阅回调函数
// prepend: 默认都是false,用于代码中listener排列的顺序
function _addListener(target, type, listener, prepend) {
  let m;
  let events; // 事件缓存
  let existing;

  // 断言:检查你穿进来的回调函数是不是函数,不是函数抛异常
  checkListener(listener);

  events = target._events;
  
  // 不存咱直接创建,兜底操作,这一步肯定跳过,咱之前实例化就创建过
  if (events === undefined) {
    events = target._events = ObjectCreate(null);
    target._eventsCount = 0;
  } else {
    
    // 如果有'newListener'事件即执行它,这里`跳过`,咱根本没有
    if (events.newListener !== undefined) {
      target.emit('newListener', type,
                  listener.listener ? listener.listener : listener);

      // 可能'newListener'会导致_event变化,确保正确性
      events = target._events;
    }
    
    // 赋值用于:检查指定的key: 'event'事件是否存在
    existing = events[type];
  }

  // 这里是第一次!!!
  // 很好,这里来校验key: 'event'事件是否存在
  if (existing === undefined) {
    // 这里不用数组是考虑到可能只有一个监听器的情况节省内存,毕竟咱v8 js对象可用内存本来就少,在底层做好优化,才能让应用层浪费~
    // 这里放入容器缓存呗,没啥的
    events[type] = listener;
    ++target._eventsCount;
  } 
  
  // 这里是第二次!!!
  else {
    
    // 这里肯定要执行,因为第一次咱们就是搞的如下结构
    /*
    	{
    		event: () => { console.log('ai yo.'); }
    	}
    */
    if (typeof existing === 'function') {
      
      // 将events容器结构改为数组并添加咱们第二次订阅的回调函数
      // 默认prepend为false,所以数组添加是顺序的
      existing = events[type] =
        prepend ? [listener, existing] : [existing, listener];
      
    } 
    // 如果是第三次插入,即结构如下
    /*
    	{
    		event: [
    			() => {},
    			() => {}
    		]
    	}
    */
    // 默认prepend为false,这里不会执行,如果为true就是从头部插入
    else if (prepend) {
      existing.unshift(listener);
    } 
    
    // 默认从尾部插入
    else {
      existing.push(listener);
    }

    // 检查订阅是否超过最大数量
    m = _getMaxListeners(target);
    
    // 超出最大数量给一个警告,最大数量为10
    if (m > 0 && existing.length > m && !existing.warned) {
      existing.warned = true;
      // No error code for this since it is a Warning
      // eslint-disable-next-line no-restricted-syntax
      const w = new Error('Possible EventEmitter memory leak detected. ' +
                          `${existing.length} ${String(type)} listeners ` +
                          `added to ${inspect(target, { depth: -1 })}. Use ` +
                          'emitter.setMaxListeners() to increase limit');
      w.name = 'MaxListenersExceededWarning';
      w.emitter = target;
      w.type = type;
      w.count = existing.length;
      process.emitWarning(w);
    }
  }

  // 返回EventEmitter自己,估计为了链式编程方便
  return target;
}

event.js#EventEmitter.prototype.emit()

发射指定事件

EventEmitter.prototype.emit = function emit(type, ...args) {
  let doError = (type === 'error'); // 判断发布的是不是'error'事件

  // 获取容器
  const events = this._events;
  
  // 容器存在内容
  if (events !== undefined) {
    
    // 执行'error'事件 `咱们不执行`
    if (doError && events[kErrorMonitor] !== undefined)
      this.emit(kErrorMonitor, ...args);
    // 执行完error打一个tag
    doError = (doError && events.error === undefined);
  } 
  
  // 不存内容且发布的'error'事件,直接返回false `跳过`
  else if (!doError)
    return false;

  // 根据上面执行'error'事件后打的tag,如果有错误抛出则执行该代码 `咱们不执行`
  if (doError) {
    let er;
    if (args.length > 0)
      er = args[0];
    if (er instanceof Error) {
      try {
        const capture = {};
        ErrorCaptureStackTrace(capture, EventEmitter.prototype.emit);
        ObjectDefineProperty(er, kEnhanceStackBeforeInspector, {
          value: enhanceStackTrace.bind(this, er, capture),
          configurable: true
        });
      } catch {}

      // Note: The comments on the `throw` lines are intentional, they show
      // up in Node's output if this results in an unhandled exception.
      throw er; // Unhandled 'error' event
    }

    let stringifiedEr;
    const { inspect } = require('internal/util/inspect');
    try {
      stringifiedEr = inspect(er);
    } catch {
      stringifiedEr = er;
    }

    // At least give some kind of context to the user
    const err = new ERR_UNHANDLED_ERROR(stringifiedEr);
    err.context = er;
    throw err; // Unhandled 'error' event
  }

  // 从这里才开始真正执行emit()逻辑
  // 获取回调函数
  const handler = events[type];

  // 没有执行的逻辑直接返回
  if (handler === undefined)
    return false;

  // 如果值为单个函数
  /*
  	{
  		event: () => {}
  	}
  */
  if (typeof handler === 'function') {
    // 执行回调
    const result = handler.apply(this, args);

    // 如果有返回值执行该方法,`跳过`
    if (result !== undefined && result !== null) {
      addCatch(this, result, type, args);
    }
  } else {
    
    // 当是函数数组时,迭代执行
    const len = handler.length;
    const listeners = arrayClone(handler);
    for (let i = 0; i < len; ++i) {
      const result = listeners[i].apply(this, args);

      if (result !== undefined && result !== null) {
        addCatch(this, result, type, args);
      }
    }
  }

  return true;
};

图解

iShot_2022-06-19_10.33.24

简单实现EventEmitter

EventEmitter使用的经典的发布者订阅者模型

传送门:简单实现EventEmitter

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值