2012-08-27 14 views
12

Sto usando l'eventemitter del nodo sebbene altri suggerimenti della libreria di eventi siano benvenuti.javascript eventemitter più eventi una volta

Voglio eseguire una funzione una volta se sono stati attivati ​​diversi eventi. Gli eventi multipli devono essere ascoltati, ma tutti vengono rimossi se uno degli eventi viene attivato. Speriamo che questo esempio di codice mostri quello che sto cercando.

var game = new eventEmitter(); 
game.once(['player:quit', 'player:disconnect'], function() { 
    endGame() 
}); 

Qual è il modo più pulito per gestire questo?

Nota: è necessario rimuovere le funzioni associate singolarmente perché ci saranno altri ascoltatori rilegati.

risposta

9

"Estendere" l'EventEmitter in questo modo:

var EventEmitter = require('events').EventEmitter; 

EventEmitter.prototype.once = function(events, handler){ 
    // no events, get out! 
    if(! events) 
     return; 

    // Ugly, but helps getting the rest of the function 
    // short and simple to the eye ... I guess... 
    if(!(events instanceof Array)) 
     events = [events]; 

    var _this = this; 

    var cb = function(){ 
     events.forEach(function(e){  
      // This only removes the listener itself 
      // from all the events that are listening to it 
      // i.e., does not remove other listeners to the same event! 
      _this.removeListener(e, cb); 
     }); 

     // This will allow any args you put in xxx.emit('event', ...) to be sent 
     // to your handler 
     handler.apply(_this, Array.prototype.slice.call(arguments, 0)); 
    }; 

    events.forEach(function(e){ 
     _this.addListener(e, cb); 
    }); 
}; 

ho creato un succo qui: https://gist.github.com/3627823 che include un esempio (il tuo esempio, con alcuni registri)

[UPDATE] Fol muggito è un adattamento della mia implementazione di once che rimuove solo l'evento che è stato chiamato, come richiesto nei commenti:

var EventEmitter = require('events').EventEmitter; 

EventEmitter.prototype.once = function(events, handler){ 
    // no events, get out! 
    if(! events) 
     return; 

    // Ugly, but helps getting the rest of the function 
    // short and simple to the eye ... I guess... 
    if(!(events instanceof Array)) 
     events = [events]; 

    var _this = this; 

    // A helper function that will generate a handler that 
    // removes itself when its called 
    var gen_cb = function(event_name){ 
     var cb = function(){ 
      _this.removeListener(event_name, cb); 
      // This will allow any args you put in 
      // xxx.emit('event', ...) to be sent 
      // to your handler 
      handler.apply(_this, Array.prototype.slice.call(arguments, 0)); 
     }; 
     return cb; 
    }; 


    events.forEach(function(e){ 
     _this.addListener(e, gen_cb(e)); 
    }); 
}; 

ho scoperto che node.js ha già il metodo once nella EventEmitter: controllare here the source code, ma questa è probabilmente un'aggiunta recente.

+0

Questo in realtà si rompe 'removeListener' di lavorare come in precedenza ha fatto. Forse questa risposta potrebbe essere espansa per risolverlo. – Harry

+0

Vuoi 'removeListener' per rimuovere solo l'evento che è stato chiamato, invece di tutti gli eventi inviati come argomento' events'? Appena avrò un'idea di quello che vuoi, aggiornerò la mia risposta. – fableal

+0

Solo l'evento inviato sarebbe fantastico. – Harry

3

Che ne dite di qualcosa di simile:

var game = new EventEmitter(); 

var handler = function() { 
    game.removeAllListeners('player:quit'); 
    game.removeAllListeners('player:disconnect'); 
    endGame(); 
}; 

game.on('player:quit', handler); 
game.on('player:disconnect', handler); 

si potrebbe scrivere un wrapper on e removeAllListeners per essere in grado di passare in un array (ad esempio, un ciclo su l'array e chiamare on o removeAllListeners per ogni elemento) .

0

Come circa il seguente approccio

var myEmitter = new CustomEventEmitter; 
myEmitter.onceAny(['event1', 'event2', 'event3', 'event4'], function() { 
     endGame(); 
}); 

quando il CustomEventEmitter assomiglia a questo

function CustomEventEmitter() {}; 

// inherit from EventEmitter 
CustomEventEmitter.prototype = new EventEmitter; 

CustomEventEmitter.prototype.onceAny = function(arrayEvents, callback) { 
    // generate unique string for the auxiliary event 
    var auxEvent = process.pid + '-' + (new Date()).getTime(); 

    // auxiliary event handler 
    var auxEmitter = new EventEmitter; 

    // handle the event emitted by the auxiliary emitter 
    auxEmitter.once(auxEvent, callback); 

    for (var i = 0; i < arrayEvents.length; i++) { 
     this.once(arrayEvents[i], function() { 
      auxEmitter.emit(auxEvent); 
     }); 
    } 

} 

La cascata di due gestori di eventi produce il risultato desiderato. Si sta utilizzando EventEmitter ausiliario ma i suoi dettagli sono ben nascosti all'interno del modulo CustomEventEmitter, in modo che l'utilizzo del modulo sia piuttosto semplice.

0

Ecco una generica funzione di supporto per gestire questo flusso di lavoro. Dovrai passare un riferimento all'oggetto emettitore di eventi, una matrice dei nomi di evento e la funzione del gestore di eventi. Tutto ciò che collega, distacca e passa argomenti al tuo gestore è curato.

// listen to many, trigger and detach all on any 
function onMany(emitter, events, callback) { 
    function cb() { 
     callback.apply(emitter, arguments); 

     events.forEach(function(ev) { 
      emitter.removeListener(ev, cb); 
     }); 
    } 

    events.forEach(function(ev) { 
     emitter.on(ev, cb); 
    }); 
} 

E un esempio di utilizzo:

// Test usage 
var EventEmitter = require('events').EventEmitter; 
var game = new EventEmitter(); 

// event list of interest 
var events = ['player:quit', 'player:disconnect']; 

// end game handler 
function endGame() { 
    console.log('end the game!'); 
} 

// attach 
onMany(game, events, endGame); 

// test. we should log 'end the game' only once 
game.emit('player:disconnect'); 
game.emit('player:quit'); 
+0

Funziona attendendo che entrambi gli eventi si verifichino prima di chiamare la richiamata – CMCDragonkai

+0

Se si emette solo uno di questi, viene ancora chiamato il callback. – CMCDragonkai

0

Ho appena incontrato qualcosa di simile e anche se per condividerle qui. Potrebbe essere che aiuta anche nel tuo caso?

ho avuto un modulo che si presentava così:

require('events').EventEmitter; 
function Foobar = {}; 
Foobar.prototype = new EventEmitter(); 
Foobar.prototype.someMethod = function() { ... } 

module.exports.getNewFoobar = function() { 
    return new Foobar(); 
}; 

La cosa divertente è: Questo ha funzionato fino al node.js 0.8.x, ma non funziona più. Il problema è questa linea:

Foobar.prototype = new EventEmitter(); 

Con questa linea tutti le istanze create condivisione sul e lo stesso EventEmitter come valori non scalari in Javascript sono sempre riferimenti. Ovviamente node.js ha cambiato alcuni comportamenti interni riguardo ai moduli come prima funzionava.

La soluzione utilizza la classe util che consente l'ereditarietà corretta.

require('events').EventEmitter; 
var util = require('util'); 

function Foobar = {}; 
util.inherits(Foobar, EventEmitter); 

Foobar.prototype.someMethod = function() { ... } 

module.exports.getNewFoobar = function() { 
    return new Foobar(); 
}; 

Spero che questo aiuti.

0

Utilizzare l'operatore del firstRx.Observable:

let game = new EventEmitter(); 
 
let events = ['player:quit', 'player:disconnect']; 
 

 
//let myObserver = Rx.Observable.fromEventPattern(handler => { 
 
// events.forEach(evt => game.on(evt, handler)); 
 
//}).first(); 
 

 
let myObserver = Rx.Observable.merge(...events.map(evt => Rx.Observable.fromEvent(game, evt))).first(); 
 

 
myObserver.subscribe(() => { 
 
    // clean up 
 
    console.log('Goodbye!!'); 
 
}); 
 

 
game.emit('player:quit'); // Goodbye!! 
 
game.emit('player:quit'); // No output 
 
game.emit('player:disconnect'); // No output
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.0/Rx.min.js"></script> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/EventEmitter/5.1.0/EventEmitter.min.js"></script>

Problemi correlati