Javascript事件发射器一次处理多个事件

Javascript eventemitter multiple events once

本文关键字:事件 一次 处理 发射器 Javascript      更新时间:2023-09-26

我正在使用节点的eventemitter,但也欢迎其他事件库建议。

如果触发了多个事件,我想运行一次函数。应监听多个事件,但如果其中任何一个事件发生火灾,则会删除所有事件。希望这个代码示例能够展示我想要的东西。

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

处理这个问题最干净的方法是什么?

注意:需要单独删除绑定的函数,因为会有其他侦听器绑定。

像这样"扩展"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在EventEmitter中已经有了一个once方法:在这里查看源代码,但这可能是最近添加的。

这样的东西怎么样:

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

您可以围绕onremoveAllListeners编写一个包装器,以便能够传入数组(例如,在数组上循环并为每个元素调用onremoveAllListeners)。

使用Rx.Observable:的first运算符

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>

不幸的是,你还没有想要类似的东西。

我看了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();
};

有趣的是:这在node.js 0.8.x之前一直有效,但现在已经不起作用了。问题是这条线:

Foobar.prototype = new EventEmitter();

通过这行,所有上创建的实例共享,并且与Javascript中的非标量值相同的EventEmitter始终是引用。很明显,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();
};

希望这能有所帮助。