web-dev-qa-db-ja.com

Node.jsで発生したすべてのイベントをリッスンする

Node.jsでは、EventEmitterオブジェクトによって発行されたallイベントをリッスンする方法はありますか?

たとえば、次のようなことができますか...

event_emitter.on('',function(event[, arg1][, arg2]...) {}

アイデアは、サーバー側EventEmitterJSON.stringifyイベントデータから吐き出されたすべてのイベントを取得し、websockets接続を介して送信し、クライアント側でイベントとして再生成することです。 、そしてクライアント側でイベントに作用します。

64
Chris W.

前述のように、この動作はnode.jsコアにはありません。ただし、hij1nxのEventEmitter2を使用できます。

https://github.com/hij1nx/EventEmitter2

EventEmitterを使用して既存のコードを壊すことはありませんが、名前空間とワイルドカードのサポートを追加します。例えば:

server.on('foo.*', function(value1, value2) {
  console.log(this.event, value1, value2);
});
38
Henrik Joreteg

私はこれが少し古いことを知っていますが、一体何があなたが取ることができる別の解決策です。

すべてのイベントをキャッチしたいエミッターの発行関数を簡単にモンキーパッチできます。

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);
  }
}

これは非常に単純なコードであり、どのエミッターでも機能するはずです。

70
Martin

ES6クラスを使用すると、非常に簡単です。

class Emitter extends require('events') {
    emit(type, ...args) {
        console.log(e + " emitted")
        super.emit(type, ...args)
    }
}
15
pravdomil

動作する可能性のある上記のすべてのソリューションには、node.js EventEmitter内部実装に対する何らかのハッキングが含まれることに注意してください。

この質問に対する正しい答えは:デフォルトのEventEmitter実装はそれをサポートしていないので、それをハックする必要があります

EventEmitterのnode.jsソースコードを見ると、特定のイベントタイプにリスナーがアタッチされていない場合、ハッシュからコールバック関数を取得しようとしているため、それ以上のアクションなしで戻るだけであることがわかります。イベントタイプに基づいて:

https://github.com/nodejs/node/blob/98819dfa5853d7c8355d70aa1aa7783677c391e5/lib/events.js#L176-L179

そのため、eventEmitter.on('*', ()=>...)のようなものはデフォルトでは機能しません。

6

すべてのライブラリで発行されたすべてのイベントをトレースする必要があったため、prototypeをタップしました。

この例では、TypeScript signature、しかし、あなたがそのようなナンセンスに興味がないなら、あなたはそれをちょうど取り除くことができます。

呼び出し内で、thisは放射しているオブジェクトを指します。私のプロジェクトのすべてのユニークなオブジェクト:エミットを追跡することは非常に簡単でした。

  // 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);
  }

これを非常に簡単に拡張し、イベント名またはクラス名に基づいてフィルタリングできます。

1
Steven Spungin

これは、Martinが上記で提供した回答に基づいています。私はノードに少し慣れていないので、自分で彼の答えを考え出す必要がありました。最後のメソッド、logAllEmitterEventsは重要なビットです。

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);
    }
}
1
Bill Heitzeg

今日同じ問題に遭遇しました、ここに解決策があります:

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);
  }
}));
0
Eladian

Node.jsのRPCモジュールを調べてください。間違っていなければ、Dnode RPCモジュールには チャットサーバー/クライアントの例 があなたがやろうとしているものに似ています。そのため、それらのモジュールを利用するか、彼らがしていることをコピーすることができます。

簡単に言うと、この例は、接続時に、接続されたクライアントからのすべてのサーバーイベントのリスナーを作成するサーバーを示しています。これは、保存されたイベント名のリストを単純に繰り返すことでこれを行います。

var evNames = [ 'joined', 'said', 'parted' ];

con.on('ready', function () {
    evNames.forEach(function (name) {
        emitter.on(name, client[name]);
    });
    emitter.emit('joined', client.name);
});

このコードは、イベントが発行されたときに、イベントに関連付けられたクライアントでリモートプロシージャコールを自動的に呼び出すため、巧妙です。

0
Nixuz

Node.js v6.0.0以降、新しいclass構文と引数のスプレッド演算子が完全にサポートされているため、単純な継承とメソッドのオーバーライドで目的の機能を実装するのは非常に安全でかなり簡単です。

'use strict';
var EventEmitter = require('events');

class MyEmitter extends EventEmitter {
  emit(type, ...args) {
    super.emit('*', ...args);
    return super.emit(type, ...args) || super.emit('', ...args);
  }
}

この実装は、イベントが何らかのリスナーによって処理されたかどうかに応じて、emitの元のEventEmitterメソッドがtrue/falseを返すという事実に依存しています。オーバーライドにreturnステートメントが含まれていることに注意してください。したがって、この動作は他のコンシューマーにも適用されます。

ここでは、スターイベント(*)すべての単一イベント(たとえば、ログ記録目的)および空のイベント('')デフォルトハンドラまたはキャッチハンドラの場合、そのイベントをキャッチするものが他にない場合に実行されます。

星(*)最初のイベント。ハンドラーのないerrorイベントの場合、結果は実際にスローされる例外です。詳細については、 EventEmitterの実装 をご覧ください。

例えば:

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

最後に、EventEmitterインスタンスが既に存在しているが、その特定のインスタンスを新しい動作に合わせて調整したい場合、次のように実行時にメソッドにパッチを適用することで簡単に実行できます。

emitter.emit = MyEmitter.prototype.emit;
0

モンキーパッチは、onAnyメソッドをEventEmitterに追加します。

1つの問題のイベントのみを監視できると便利です。

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)})
0
Shimon Doodkin