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 on
méthode sur le prototype et exécute réellement _addListener
cette 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;
}