javascript eventemitter несколько событий один раз
Я использую eventemitter узла, хотя другие предложения библиотеки событий приветствуются.
Я хочу запустить функцию один раз, если запущено несколько событий. Несколько событий должны прослушиваться, но все они удаляются, если какое-либо из событий срабатывает. Надеюсь, этот пример кода демонстрирует то, что я ищу.
var game = new eventEmitter();
game.once(['player:quit', 'player:disconnect'], function () {
endGame()
});
что такое чистый способ справиться с этим?
Примечание: необходимо удалить связанные функции по отдельности, потому что будут другие слушатели связаны.
7 ответов
"расширить" 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));
});
};
я обнаружил этот узел.js уже имеет once
метод в EventEmitter: проверка вот исходный код, но это, вероятно, недавнее дополнение.
как насчет чего-то вроде этого:
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
для каждого элемента).
Unfortunalely нет ничего похожего вы хотите еще.
Я смотрю на EventEmitter2 но есть проблема, я считаю... (#66, #31)
Как насчет следующего подхода
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, так что использование модуля довольно просто.
вот общая вспомогательная функция для обработки этого рабочего процесса. Вам нужно будет передать ссылку на объект эмиттера событий, массив имен событий и функцию обработчика событий. Все прикрепление, отсоединение и передача аргументов обработчику выполняются.
// 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');
Я просто столкнулся с чем-то подобным и, хотя поделиться им здесь. Может быть, это также помогает в вашем случае?
у меня был модуль, который выглядел вот так:
require('events').EventEmitter;
function Foobar = {};
Foobar.prototype = new EventEmitter();
Foobar.prototype.someMethod = function() { ... }
module.exports.getNewFoobar = function() {
return new Foobar();
};
самое смешное: это работало до узла.js 0.8.x, но больше не работает. Проблема заключается в следующем:
Foobar.prototype = new EventEmitter();
строка все созданные экземпляры share on и тот же EventEmitter, что и не скалярные значения в Javascript, всегда являются ссылками. Очевидно узел.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();
};
надеюсь, что это помогает.
использовать 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>