javascript eventemitter 一次触发多个事件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12150540/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
javascript eventemitter multiple events once
提问by Harry
I'm using node's eventemitter though other event library suggestions are welcomed.
尽管欢迎其他事件库建议,但我正在使用节点的事件发射器。
I want to run a function once if several events are fired. Multiple events should be listened to, but all of them are removed if any one of the events fires. Hopefully this code sample demonstrates what I'm looking for.
如果触发了多个事件,我想运行一次函数。应该侦听多个事件,但如果其中任何一个事件触发,则所有事件都将被删除。希望此代码示例展示了我正在寻找的内容。
var game = new eventEmitter();
game.once(['player:quit', 'player:disconnect'], function () {
endGame()
});
What is the cleanest way to handle this?
处理这个问题的最干净的方法是什么?
Note: Need to remove bound functions individually because there will be other listeners bound.
注意:需要单独移除绑定函数,因为会绑定其他监听器。
采纳答案by fableal
"Extend" the EventEmitter like this:
像这样“扩展”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);
});
};
I created a gist here: https://gist.github.com/3627823which includes an example (your example, with some logs)
我在这里创建了一个要点:https: //gist.github.com/3627823,其中包括一个示例(您的示例,带有一些日志)
[UPDATE]
Following is an adaptation of my implementation of once
that removes only the event that was called, as requested in the comments:
[更新]以下是我的实现的改编,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));
});
};
I found out that node.js already has a once
method in the EventEmitter: check here the source code, but this is probably a recent addition.
我发现 node.jsonce
在 EventEmitter 中已经有一个方法:在这里查看源代码,但这可能是最近添加的。
回答by Michelle Tilley
How about something like this:
这样的事情怎么样:
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);
You could write a wrapper around on
and removeAllListeners
to be able to pass in an array (e.g., loop over the array and call on
or removeAllListeners
for each element).
您可以编写一个包装器on
并removeAllListeners
能够传入一个数组(例如,遍历数组并调用on
或removeAllListeners
为每个元素)。
回答by langpavel
Unfortunalely there is nothing similar you wish yet.
不幸的是,还没有您想要的类似内容。
I look at EventEmitter2but there is an issue i believe... (#66, #31)
我看着EventEmitter2但我相信有一个问题......(#66,#31)
回答by Arik G
How about the following approach
下面的方法怎么样
var myEmitter = new CustomEventEmitter;
myEmitter.onceAny(['event1', 'event2', 'event3', 'event4'], function() {
endGame();
});
when the CustomEventEmitter looks like this
当 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);
});
}
}
The cascading of two event handlers produces the desired result. You are using an auxiliary EventEmitter but its details are well concealed within the CustomEventEmitter module, so that the usage of the module is pretty straightforward.
两个事件处理程序的级联会产生所需的结果。您正在使用一个辅助 EventEmitter 但它的细节很好地隐藏在 CustomEventEmitter 模块中,因此该模块的使用非常简单。
回答by Ben Taber
Here's a generic helper function to handle this workflow. You'll need to pass a reference to the event emitter object, an array of the event names, and your event handler function. All attaching, detaching, and passing arguments through to your handler is taken care of.
这是处理此工作流的通用辅助函数。您需要传递对事件发射器对象的引用、事件名称数组和事件处理函数。所有附加、分离和传递参数到您的处理程序都会得到处理。
// 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);
});
}
And a usage example:
以及一个使用示例:
// 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');
回答by Dominik Ehrenberg
I just ran into something similar and though to share it here. Could be that it also helps in your case?
我刚刚遇到了类似的事情,尽管在这里分享。可能它对你的情况也有帮助吗?
I had a module that looked like this:
我有一个看起来像这样的模块:
require('events').EventEmitter;
function Foobar = {};
Foobar.prototype = new EventEmitter();
Foobar.prototype.someMethod = function() { ... }
module.exports.getNewFoobar = function() {
return new Foobar();
};
The funny thing is: This worked up until node.js 0.8.x but doesn't work anymore. The problem is this line:
有趣的是:这一直工作到 node.js 0.8.x 但不再工作了。问题是这一行:
Foobar.prototype = new EventEmitter();
With this line allthe instances created share on and the same EventEmitter as non-scalar values in Javascript are always references. Obviously node.js changed some internal behavior regarding modules as this worked before.
使用这一行,所有创建的实例都共享,并且与 Javascript 中的非标量值相同的 EventEmitter 始终是引用。显然 node.js 改变了一些关于模块的内部行为,因为这在以前是有效的。
Solution is using the util class which allows correct inheritance.
解决方案是使用允许正确继承的 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();
};
Hope this helps.
希望这可以帮助。
回答by smac89
Use the first
operator of Rx.Observable
:
使用以下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>