2012-08-27 25 views
12

Korzystam z eventemitter węzła, choć inne sugestie biblioteki zdarzeń są mile widziane.Zdarzenia wielokrotnego zdarzenia javascript wielokrotnego zdarzenia raz

Chcę uruchomić funkcję raz, jeśli kilka zdarzeń jest uruchamianych. Należy wysłuchać wielu zdarzeń, ale wszystkie są usuwane, jeśli wystąpi jedno z wydarzeń. Mam nadzieję, że ten przykład kodu pokazuje, czego szukam.

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

Jaki jest najczystszy sposób poradzić sobie z tym?

Uwaga: Należy usunąć powiązane funkcje pojedynczo, ponieważ będą powiązane inne odbiorniki.

Odpowiedz

9

"Extend" THE EventEmitter tak:

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

I stworzył istotę tutaj: https://gist.github.com/3627823 który zawiera przykład (twój przykład, z pewnymi logami)

[UPDATE] Fol ryk jest adaptacją mojego wykonania once że usuwa tylko zdarzenie, które nazwano, zgodnie z wnioskiem w komentarzach:

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

I okazało się, że node.js ma już once metody w EventEmitter: sprawdź here the source code, ale jest to prawdopodobnie ostatni dodatek.

+0

To faktycznie łamie 'removeListener' z pracy jak to wcześniej zrobił. Może ta odpowiedź może zostać rozszerzona, aby to naprawić. – Harry

+0

Czy chcesz, aby 'removeListener' usuwał tylko wywoływane zdarzenie, zamiast wszystkich zdarzeń, które są wysyłane jako argumenty' events'? Jak tylko zorientuję się, czego chcesz, zaktualizuję odpowiedź. – fableal

+0

Po prostu wydarzenie, które zostało wysłane, byłoby wspaniałe. – Harry

3

Jak o coś takiego:

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

można napisać otoki wokół on i removeAllListeners aby móc przekazać w tablicy (np pętli nad tablicy i zadzwonić on lub removeAllListeners dla każdego elementu) .

0

Jak o następujące podejście

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

gdy CustomEventEmitter wygląda to

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

} 

kaskadowego dwóch obsługi zdarzeń wywołuje pożądany rezultat. Korzystasz z pomocniczego EventEmitter, ale jego szczegóły są dobrze ukryte w module CustomEventEmitter, dzięki czemu korzystanie z modułu jest dość proste.

0

Oto ogólna funkcja pomocnicza do obsługi tego przepływu pracy. Musisz przekazać odwołanie do obiektu emitera zdarzeń, tablicy nazw zdarzeń i funkcji obsługi zdarzeń. Wszystkie załatwianie, odłączanie i przekazywanie argumentów do twojego przewodnika jest załatwione.

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

I Przykład użycia:

// 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

Czy to działa, czekając na oba zdarzenia przed wywołaniem oddzwonienia – CMCDragonkai

+0

Jeśli emitujesz tylko jeden z nich, wywołanie zwrotne jest nadal wywoływane. – CMCDragonkai

0

po prostu wpadł na coś podobnego i choć to zrobić tutaj. Czy to możliwe, że pomaga w twoim przypadku?

miałem moduł, który wyglądał tak:

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

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

Najśmieszniejsze jest: To działało aż node.js 0.8.x ale nie działa już. Problemem jest to linia:

Foobar.prototype = new EventEmitter(); 

Z tej linii wszystkie instancje utworzone na udział i to samo EventEmitter jako wartości nieskalarnych w javascript zawsze są referencje. Oczywiście węzeł.js zmienił niektóre wewnętrzne zachowania dotyczące modułów, ponieważ działało to wcześniej.

Rozwiązanie wykorzystuje klasę util który umożliwia prawidłowe dziedziczenie.

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

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

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

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

Mam nadzieję, że to pomoże.

0

użyć operatora first z Rx.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>

Powiązane problemy