Dans Node.js, existe-t-il un moyen d'écouter les événements tous émis par un objet EventEmitter?
par exemple, pouvez-vous faire quelque chose comme ...
event_emitter.on('',function(event[, arg1][, arg2]...) {}
L'idée est de saisir tous les événements générés par un serveur EventEmitter
, JSON.stringify
les données d'événement, de les envoyer via une connexion websockets, de les reformer côté client en tant qu'événement, puis d'agir sur l'événement le côté client.
Comme mentionné, ce comportement n’est pas dans le noyau node.js. Mais vous pouvez utiliser EventEmitter2 de hij1nx:
https://github.com/hij1nx/EventEmitter2
Cela ne cassera pas le code existant en utilisant EventEmitter, mais ajoutera un support pour les espaces de noms et les caractères génériques. Par exemple:
server.on('foo.*', function(value1, value2) {
console.log(this.event, value1, value2);
});
Je sais que c'est un peu vieux, mais bon sang, voici une autre solution que vous pourriez prendre.
Vous pouvez facilement patcher la fonction d’émission de l’émetteur pour lequel vous souhaitez intercepter tous les événements:
function patchEmitter(emitter, websocket) {
var oldEmit = emitter.emit;
emitter.emit = function() {
var emitArgs = arguments;
// serialize arguments in some way.
...
// send them through the websocket received as a parameter
...
oldEmit.apply(emitter, arguments);
}
}
C'est un code assez simple et devrait fonctionner sur n'importe quel émetteur.
Avec les cours ES6, c'est très simple:
class Emitter extends require('events') {
emit(type, ...args) {
console.log(e + " emitted")
super.emit(type, ...args)
}
}
Sachez que toutes les solutions décrites ci-dessus qui pourraient fonctionner impliqueront une sorte de piratage pour la mise en œuvre interne de node.js EventEmitter.
La bonne réponse à cette question serait: l'implémentation par défaut de EventEmitter ne supporte pas cela, vous devez la modifier.
Si vous examinez le code source de node.js pour EventEmitter, vous verrez que lorsqu'aucun écouteur n'est attaché à un type d'événement spécifique, il sera simplement renvoyé sans autre action, car il tente de récupérer la fonction de rappel à partir d'un hachage. en fonction du type d'événement:
https://github.com/nodejs/node/blob/98819dfa5853d7c8355d70aa1aa7783677c391e5/lib/events.js#L176-L179
C'est pourquoi quelque chose comme eventEmitter.on('*', ()=>...)
ne peut pas fonctionner par défaut.
Je devais retracer tous les événements émis dans toutes les bibliothèques, alors j'ai exploité la variable prototype
.
Cet exemple utilise un TypeScript signature
, mais vous pouvez simplement le supprimer si vous n'êtes pas dans ce genre de bêtises.
Dans l'appel, this
fait référence à l'objet qui émet. Il était très facile de suivre tout objet unique: émet dans mon projet.
// For my example I use a `set` to track unique emits.
const items = new Set()
const originalEmit = EventEmitter.prototype.emit;
EventEmitter.prototype.emit = function (event: String | Symbol, ...args: any[]): boolean {
// Do what you want here
const id = this.constructor.name + ":" + event;
if (!items.has(id)) {
items.add(id);
console.log(id);
}
// And then call the original
return originalEmit.call(event, ...args);
}
Vous pouvez très facilement étendre cela et filtrer en fonction du nom de l'événement ou du nom de la classe.
Ceci est basé sur la réponse fournie par Martin ci-dessus. Je suis un peu nouveau sur le noeud, donc je devais élaborer sa réponse pour moi-même. La méthode à la fin, logAllEmitterEvents est le bit important.
var events = require('events');
var hungryAnimalEventEmitter = new events.EventEmitter();
function emitHungryAnimalEvents()
{
hungryAnimalEventEmitter.emit("HungryCat");
hungryAnimalEventEmitter.emit("HungryDog");
hungryAnimalEventEmitter.emit("Fed");
}
var meow = function meow()
{
console.log('meow meow meow');
}
hungryAnimalEventEmitter.on('HungryCat', meow);
logAllEmitterEvents(hungryAnimalEventEmitter);
emitHungryAnimalEvents();
function logAllEmitterEvents(eventEmitter)
{
var emitToLog = eventEmitter.emit;
eventEmitter.emit = function () {
var event = arguments[0];
console.log("event emitted: " + event);
emitToLog.apply(eventEmitter, arguments);
}
}
A couru dans le même problème aujourd'hui, voici une solution:
Object.create(Object.assign({},EventEmitter.prototype, {
_onAnyListeners:[],
emit:function(...args){
//Emit event on every other server
if(this._fireOnAny && typeof this._fireOnAny === 'function'){
this._fireOnAny.apply(this,args)
}
EventEmitter.prototype.emit.apply(this,args)
},
_fireOnAny:function(...args){
this._onAnyListeners.forEach((listener)=>listener.apply(this,args))
},
onAny:function(func){
if(typeof func !== 'function'){
throw new Error('Invalid type');
}
this._onAnyListeners.Push(func);
},
removeOnAny:function(func){
const index = this._onAnyListeners.indexOf(func);
if(index === -1){
return;
}
this._onAnyListeners.splice(index,1);
}
}));
Vous voudrez peut-être examiner les modules RPC pour node.js. Si je ne me trompe pas, le module Dnode RPC a un exemple de serveur/client de discussion similaire à ce que vous essayez de faire. Vous pouvez donc utiliser leur module ou copier ce qu’ils font.
En résumé, l'exemple montre un serveur qui, lors de la connexion, crée des écouteurs pour tous les événements de serveur du client connecté. Pour ce faire, il suffit de parcourir une liste de noms d’événements stockée.
var evNames = [ 'joined', 'said', 'parted' ];
con.on('ready', function () {
evNames.forEach(function (name) {
emitter.on(name, client[name]);
});
emitter.emit('joined', client.name);
});
Ce code est intelligent car il appelle automatiquement un appel de procédure à distance sur le client associé à l'événement lors de son émission.
Depuis Node.js v6.0.0, les nouveaux opérateurs de propagation de syntaxe et d'argument class
sont entièrement pris en charge. Il est donc assez sûr et simple d'implémenter les fonctionnalités souhaitées avec un simple héritage et un remplacement de méthode:
'use strict';
var EventEmitter = require('events');
class MyEmitter extends EventEmitter {
emit(type, ...args) {
super.emit('*', ...args);
return super.emit(type, ...args) || super.emit('', ...args);
}
}
Cette implémentation repose sur le fait que la méthode emit
originale de EventEmitter
renvoie true
/false
selon que l'événement a été géré par un écouteur ou non. Notez que le remplacement inclut une instruction return
. Nous conservons donc ce comportement pour les autres consommateurs.
Ici, l’idée est d’utiliser l’événement star (*
) pour créer des gestionnaires exécutés pour chaque événement (par exemple, à des fins de journalisation) et l’événement vide (''
) pour un gestionnaire par défaut ou intercepter tout, qui s’exécute sans autre tâche. attrape cet événement.
Nous nous assurons d'appeler d'abord l'événement star (*
), car dans le cas d'événements error
sans aucun gestionnaire, le résultat est en réalité une exception générée. Pour plus de détails, jetez un coup d'œil à la mise en oeuvre de EventEmitter
.
Par exemple:
var emitter = new MyEmitter();
emitter.on('foo', () => console.log('foo event triggered'));
emitter.on('*', () => console.log('star event triggered'));
emitter.on('', () => console.log('catch all event triggered'));
emitter.emit('foo');
// Prints:
// star event triggered
// foo event triggered
emitter.emit('bar');
// Prints:
// star event triggered
// catch all event triggered
Enfin, si une instance EventEmitter existe déjà mais que vous souhaitez ajuster cette instance spécifique au nouveau comportement, vous pouvez le faire facilement en appliquant un correctif à la méthode au moment de l'exécution, comme suit:
emitter.emit = MyEmitter.prototype.emit;
un patch de singe ajoute une méthode à EventEmitter.
il est utile de ne pouvoir surveiller que les événements d'un problème.
var EventEmitter=require('events')
var origemit=EventEmitter.prototype.emit;
Object.assign( EventEmitter.prototype, {
emit:function(){
if(this._onAnyListeners){
this._onAnyListeners.forEach((listener)=>listener.apply(this,arguments))
}
return origemit.apply(this,arguments)
},
onAny:function(func){
if(typeof func !== 'function'){
throw new Error('Invalid type');
}
if(!this._onAnyListeners)this._onAnyListeners=[];
this._onAnyListeners.Push(func);
},
removeOnAny:function(func){
const index = this._onAnyListeners.indexOf(func);
if(index === -1){
return;
}
this._onAnyListeners.splice(index,1);
}
});
// usage example
//gzip.onAny(function(a){console.log(a)})