Apprenez à publier et à vous abonner à partir du code source du nœud

Insérez la description de l'image ici

Qu'est-ce que publier et s'abonner

Dans l'architecture logicielle, publier et souscrire est un paradigme de message, l'expéditeur du message (appelé l'éditeur) n'envoie pas le message directement au destinataire spécifique (appelé l'abonné). Au lieu de cela, les messages publiés sont divisés en différentes catégories sans savoir quels abonnés (le cas échéant) peuvent exister. De même, les abonnés peuvent exprimer leur intérêt pour une ou plusieurs catégories et ne recevoir que des messages d'intérêt sans savoir quels éditeurs (le cas échéant) existent.

Le modèle de publication-abonnement peut être considéré comme 观察者模式une extension. Personnellement, le modèle de publication-abonnement est中介控制的

Objectif: définir une relation de dépendance un-à-plusieurs entre les objets. Lorsque l'état d'un objet change, tous les objets qui en dépendent sont notifiés et automatiquement mis à jour.

Publier et abonner EventEmitter dans le nœud

Regardez d'abord l'utilisation du document

const myEE = new EventEmitter();
myEE.on('foo', () => console.log('a'));
myEE.emit('foo');

Abonnez-vous sur

EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.addListener = function addListener(type, listener) {
    
    
  return _addListener(this, type, listener, false);
};

EventEmitter définit la onméthode sur le prototype et exécute réellement _addListenercette fonction en interne.

function _addListener(target, type, listener, prepend) {
    
    
  // (●'◡'●) 最多几个监听器
  let m;
  // (●'◡'●) 监听事件
  let events;
  // (●'◡'●) 存档监听器的容器
  let existing;
  // (●'◡'●) 用于检查listener是不是function    
  checkListener(listener);
  // (●'◡'●) 同步对象上已经有的监听事件
  events = target._events;
  // (●'◡'●) 如果不存在 就先初始化
  if (events === undefined) {
    
    
    events = target._events = ObjectCreate(null);
    target._eventsCount = 0;
  } else {
    
    
    //(●'◡'●) !!!!防止循环调用  
    //EventEmitter 实例在新的监听器被添加到其内部监听器数组之前,会触发自身的 'newListener' 事件
    if (events.newListener !== undefined) {
    
    
      target.emit('newListener', type,
                  listener.listener ? listener.listener : listener);

      // Re-assign `events` because a newListener handler could have caused the
      // this._events to be assigned to a new object
      events = target._events;
    }
    existing = events[type];
  }
    
    /**
    *(●'◡'●) 这一段就是如果还没有就直接设置 如果有就变成数组(我们自己写的时候偷懒可以直接写成一个数组)
    */
  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);
    }

    /**
    *(●'◡'●) 这一段是检查最大监听器数量的 如果溢出就抛出一个错误 
    */
    m = _getMaxListeners(target);
    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);
    }
  }

  return target;
}

A partir du code ci-dessus, on peut savoir que la réalisation de l'abonnement nécessite une clé d'objet de carte d'événement pour stocker l'écouteur pour la valeur d'événement

Libérer émettre

EventEmitter.prototype.emit = function emit(type, ...args) {
    
    
  //(●'◡'●) 是不是'error'找个特殊类型
  let doError = (type === 'error');
      
  const events = this._events;
  
    /**
    *(●'◡'●) 这一段处理了一下 有错误事件的情况
    */
  if (events !== undefined) {
    
    
  // kErrorMonitor的定义是这样的 const kErrorMonitor = Symbol('events.errorMonitor');
    if (doError && events[kErrorMonitor] !== undefined)
      this.emit(kErrorMonitor, ...args);
    doError = (doError && events.error === undefined);
  } else if (!doError)
    return false;

  // If there is no 'error' event listener then throw.
    /**
    *(●'◡'●) 这一段 如果没有监听事件 但是发射了一个error 抛错
    */
  if (doError) {
    
    
    let er;
    if (args.length > 0)
      er = args[0];
    if (er instanceof Error) {
    
    
      try {
    
    
        const capture = {
    
    };
        // eslint-disable-next-line no-restricted-syntax
        Error.captureStackTrace(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
  }

    /**
    *(●'◡'●) 这里开始进入正题
    */
  const handler = events[type];

  //(●'◡'●) 没处理函数直接return
  if (handler === undefined) 
    return false;

  if (typeof handler === 'function') {
    
    
    //(●'◡'●) 这里没法直接读到源码 原因 https://stackoverflow.com/questions/59750976/what-are-primordials-in-node-js
    //这里应该猜测是调用handler方法 类似于https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/apply
    const result = ReflectApply(handler, this, args);

    // We check if result is undefined first because that
    // is the most common case so we do not pay any perf
    // penalty
    
    //(●'◡'●) 我们首先检查结果是否未定义,因为最常见的就是这种情况
    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 = ReflectApply(listeners[i], this, args);

      // We check if result is undefined first because that
      // is the most common case so we do not pay any perf
      // penalty.
      // This code is duplicated because extracting it away
      // would make it non-inlineable.
      if (result !== undefined && result !== null) {
    
    
        addCatch(this, result, type, args);
      }
    }
  }

  return true;
};

La façon dont la fonction addCatch est utilisée pour détecter les erreurs est également tout un spectacle. . .

function addCatch(that, promise, type, args) {
    
    
  if (!that[kCapture]) {
    
    
    return;
  }

  // Handle Promises/A+ spec, then could be a getter
  // that throws on second use.
  try {
    
    
    const then = promise.then;

    if (typeof then === 'function') {
    
    
      then.call(promise, undefined, function(err) {
    
    
        // The callback is called with nextTick to avoid a follow-up
        // rejection from this promise.
        process.nextTick(emitUnhandledRejectionOrErr, that, err, type, args);
      });
    }
  } catch (err) {
    
    
    that.emit('error', err);
  }
}

une fois que

L'implémentation principale de once est de lier un objet externe avec une propriété déclenchée true lorsqu'il n'exécute pas et supprime la fonction de surveillance.

EventEmitter.prototype.once = function once(type, listener) {
    
    
  checkListener(listener);

  this.on(type, _onceWrap(this, type, listener));
  return this;
};

function onceWrapper() {
    
    
  if (!this.fired) {
    
    
    this.target.removeListener(this.type, this.wrapFn);
    this.fired = true;
    if (arguments.length === 0)
      return this.listener.call(this.target);
    return this.listener.apply(this.target, arguments);
  }
}

function _onceWrap(target, type, listener) {
    
    
  const state = {
    
     fired: false, wrapFn: undefined, target, type, listener };
  const wrapped = onceWrapper.bind(state);
  wrapped.listener = listener;
  state.wrapFn = wrapped;
  return wrapped;
}

Je suppose que tu aimes

Origine blog.csdn.net/weixin_38616850/article/details/108721015
conseillé
Classement