【问题标题】:Javascript eventemitter multiple events onceJavascript eventemitter 多个事件一次
【发布时间】:2012-08-22 10:42:42
【问题描述】:

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

如果触发了多个事件,我想运行一次函数。应该监听多个事件,但如果任何一个事件触发,它们都会被删除。希望此代码示例演示了我正在寻找的内容。

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

最干净的处理方法是什么?

注意:需要单独移除绑定的函数,因为还会绑定其他监听器。

【问题讨论】:

    标签: javascript node.js dom-events eventemitter


    【解决方案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 的实现的改编,它只删除了被调用的事件,如 cmets 中所要求的:

    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 方法:检查here the source code,但这可能是最近添加的。

    【讨论】:

    • 这实际上使removeListener 无法像以前那样工作。也许这个答案可以扩展来解决这个问题。
    • 您是否希望removeListener 仅删除被调用的事件,而不是作为events 参数发送的所有事件?一旦我知道你想要什么,我会更新我的答案。
    • 只是发送的事件会很棒。
    • 是否只有在所有事件都发生时才运行回调?
    【解决方案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);
    

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

    【讨论】:

      【解决方案3】:

      使用Rx.Observablefirst 运算符:

      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>

      【讨论】:

        【解决方案4】:

        很遗憾,您还没有任何类似的东西。

        我查看了EventEmitter2,但我认为有一个问题......(#66#31

        【讨论】:

          【解决方案5】:

          下面的方法怎么样

          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 模块中,因此该模块的使用非常简单。

          【讨论】:

            【解决方案6】:

            这是处理此工作流程的通用辅助函数。您需要传递对事件发射器对象的引用、事件名称数组和事件处理函数。处理所有附加、分离和传递参数到您的处理程序。

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

            【讨论】:

            • 在调用回调之前等待这两个事件发生是否有效
            • 如果你只发出其中一个,回调仍然会被调用。
            【解决方案7】:

            我刚遇到类似的事情,但在这里分享一下。可能它对您的情况也有帮助吗?

            我有一个看起来像这样的模块:

            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 这一行中,创建的实例与 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();
            };
            

            希望这会有所帮助。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-07-15
              • 2019-04-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-10-15
              相关资源
              最近更新 更多