Подтвердить что ты не робот

Javascript eventemitter несколько событий один раз

Я использую node eventemitter, но приветствуются другие предложения библиотеки событий.

Я хочу запустить функцию один раз, если запущено несколько событий. Следует прослушивать несколько событий, но все они удаляются, если происходит какое-либо из событий. Надеюсь, этот пример кода демонстрирует то, что я ищу.

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

Каков самый чистый способ справиться с этим?

Примечание. Необходимо удалить связанные функции отдельно, потому что будут связаны другие слушатели.

4b9b3361

Ответ 1

"Расширьте" EventEmitter следующим образом:

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

Я создал суть здесь: https://gist.github.com/3627823 который включает пример (ваш пример, с некоторыми журналами)

[ ОБНОВЛЕНИЕ] Ниже приведена адаптация моей реализации once, которая удаляет только событие, которое было вызвано, как это запрошено в комментариях:

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

Я узнал, что node.js уже имеет метод once в EventEmitter: проверьте здесь исходный код, но это вероятно, является недавним дополнением.

Ответ 2

Как насчет чего-то вроде этого:

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

Вы можете написать обертку вокруг on и removeAllListeners, чтобы иметь возможность передавать в массив (например, цикл по массиву и вызов on или removeAllListeners для каждого элемента).

Ответ 3

К несчастью, ничего подобного вам пока не нужно.

Я смотрю EventEmitter2, но есть проблема, которую я считаю... (# 66, # 31)

Ответ 4

Как насчет следующего подхода

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

когда CustomEventEmitter выглядит следующим образом

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

}

Каскадирование двух обработчиков событий дает желаемый результат. Вы используете вспомогательный EventEmitter, но его детали хорошо скрыты в модуле CustomEventEmitter, так что использование модуля довольно просто.

Ответ 5

Здесь используется общая вспомогательная функция для обработки этого рабочего процесса. Вам нужно будет передать ссылку на объект эмиттера события, массив имен событий и функцию обработчика событий. Все прикрепление, отключение и передача аргументов вашему обработчику соблюдаются.

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

И пример использования:

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

Ответ 6

Я просто столкнулся с чем-то подобным и хотя бы поделиться им здесь. Может быть, это также помогает в вашем случае?

У меня был модуль, который выглядел так:

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

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

Самое смешное: это работало до node.js 0.8.x, но больше не работает. Проблема заключается в этой строке:

Foobar.prototype = new EventEmitter();

С помощью этой строки all экземпляры, созданные совместно, и тот же EventEmitter, что и нескалярные значения в Javascript, всегда являются ссылками. Очевидно, что node.js изменило некоторое внутреннее поведение в отношении модулей, как это было раньше.

Решение использует класс util, который позволяет правильно наследовать.

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

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

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

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

Надеюсь, что это поможет.

Ответ 7

Используйте first оператор 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>