I have an object оf inherited from EventEmiter "class".
It has many events (emitter.on) and I don't know their names.
How can I get their names? And how can I handle ALL events?
You can not programatically get all possible events that will be emitted on a specific event emitter. You may be able to do so by reading the source code, however.
The only way to handle all events at runtime, that I know of, is to overwrite the emit function for that one EventEmitter. Your code will then be called whenever an event gets emitted, and you can forward it to the original function.
var EventEmitter = require("events").EventEmitter
var emitter = new EventEmitter();
emitter.on('test', function(t) {
console.log('Handled test', t);
});
var old_emit = emitter.emit;
emitter.emit = function() {
console.log("Intercepted", arguments);
old_emit.apply(emitter, arguments);
}
emitter.emit('test', 'hi');
emitter.emit('something', 'else');
demo: http://ideone.com/RfqFvx
EventEmitter has own events as well, one of them: newListener, which will pass event name and callback function when new listener is added.
Additionally you can use emitter.listeners in order to get list of callback functions for specific event name.
You might want to use one event name, and send object with identification of object name, that way you can have one event, but response to it differently.
Related
I have EventEmitter and socket.io working in tandem and they work as I am expecting them to work. I have an object, that contains data, that data can randomly change at any point in time, when the data changes, it fires an EventEmitter.emit with the updated data.
In another file I have an EventEmitter.on which captures that data and passes it on to socket.emit to be sent to the front end, something like this:
Within the object containing the data:
UpdateIfDataIsNew(newData, oldData) {
if (newData !== oldData) { //simplified, not exactly like this
eventBus.emit('dataEvent', newData)
}
}
Where eventBus is a global EventEmitter object.
And in my file handling the sending of the data to the front end, the code is roughly:
ioServer.on("connection", (socket) => {
eventBus.on("dataEvent", (data) => {
socket.emit("frontEndDataEvent", data);
});
})
The way that this is working, whenever a new user connects, a new socket is created, and a new socket.emit is added to the list of functions to be fired when "dataEvent" is triggered, so that all connected users receive the updated data. The problem is that I don't know how to remove a particular socket.emit from the eventBus array of functions when a particular user disconnects, because when I don't do that, all functions stay in the event array, and all of them fire when an event is triggered, althought these users are no longer on the website, eventually the array gets incredibly big, even a simple refresh from a single user, keeps adding more and more functions to the array.
I know that I can see if a user has disconnected with socket.on("disconnect", ???) but then what?
http://prntscr.com/tou4pw the functions are the same, just with a different socket, how do I find the one that must be removed, when someone disconnects?
Instead of just adding a function to the eventEmitter, can I add say an object that contains an ID: and the function, so that I can then quickly identify the one that I must remove?
I solved it by creating my own Event Emitter class (externding EventEmitter), that way I have control over the array of functions for a given event and am also able to assign an ID.
const EventEmitter = require("events");
class RobotData extends EventEmitter {
constructor() {
super();
this.emitters = [];
}
add(socket, dataType) {
this.emitters.push({
socket,
dataType,
trigger: (eventName, newData) => {
socket.emit(`${eventName}`, newData);
},
});
}
remove(ID) {
this.emitters = this.emitters.filter((emitter) => emitter.socket.id !== ID);
}
}
const robotData = new RobotData();
module.exports = { robotData };
And then in the disconnect event:
socket.on("disconnect", () => {
robotData.remove(socket.id);
});
Bot library for Discord,
bot.on('messageCreate', msg => {
// do some stuff
bot.removeListener('messageCreate', msg);
});
An error I get often is listener must be a function.
messageCreate is fired when a message is received, msg is a reference to the message class, which contains the message id, author, etc.
What I'm trying to get is the bot waiting for a specific reply back from the user, such as Yes or No.
If bot is a Node's EventEmitter, you can use once method instead of on so that it automatically removes the listener after the listener is called once.
bot.once('messageCreate', msg => {
// do some stuff
// you don't need to remove the listener by yourself!
});
To straightly answer your question, the second argument of removeListener must be the listener function that you passed to on.
var listener = msg => {
// do some stuff
bot.removeListener('messageCreate', listener);
};
bot.on('messageCreate', listener);
In discord V12 you can remove the listener like this inside the ready event if you are using some other type of command handler like commando
bot.once("ready", () => {
bot.removeListener("message", client._events.message[0]);
}
I realise that nodejs has a powerful EventEmitter constructor which allows you to emit events. However, what EventEmitter is missing is a way for the event emitter to see what the listeners returned.
This is the functionality I am after:
e = new FantasticEventEmitter();
e.on( 'event1', function( param1, param2, cb ){
console.log("First listener called...")
cb( null, 10 );
});
e.on( 'event1', function( param2, param2, cb ){
console.log("Ah, another listener called!");
cb( null, 20 );
});
e.emit( 'event1', 'firstParameter', 'secondParameter', function( err, res ){
console.log("Event emitted, now res will be [ 10, 20]");
});
Basically, I want listeners to be able to register, getting called when an event is fired up, and:
Listeners to be passed a callback parameter. the callback will "collect" the results
Emitters to have a callback, which will get called with the result collections
Is there a library that does this already, before I reinvent the wheel?
There is no easy way to do it. To allow what I asked for, I wrote this GutHub module:
EventEmitterCollector.
Enjoy...
Correct me if I am wrong but I don't think it should be. Event Emitter should not return callback results. Eventemiiters are an alternative to asynchronous function calls, if you use them correctly. You should not try to combine them and complicate stuff.
If you use them properly you can do what you want without async. There is no generalized way with eventemitter. Here is how I would do it :
Event incoming : process the parameters and trigger stepn in any fashion.
Event stepn : different for each step, do each step and at end call collect or call another intermediate step.
Event collect : store the outputs check for completion and call done when
finished.
Event done : final callback (can be done in the collect itself).
This is totally my opinion/what i learned : asynchronous callbacks are the end result of internally evented execution. If you were to implement a async job at low-level, this is how you would do it. At high level with function callbacks and stuff, you would use async.
Maybe I'm misinterpreting your question, but your desired result code from your original question should just work. You can pass in a callback function as a parameter. EventEmitter instances you define can take callback arguments already. I made an example that shows how to do what you wanted with little change to your original.
var EventEmitter = require('events').EventEmitter;
var e = new EventEmitter();
// first listener
e.on('whatever', function(x,cb) {
console.log('first listener');
cb(10);
});
// second listener
e.on('whatever', function(x,cb) {
console.log('second listener');
cb(20);
});
// emit logic
var res = [];
e.emit('whatever', 'parameter', function(val) {
res.push(val);
});
console.log('Event emitted, now res will be [' + res.join(',') + ']');
My use case is to read RSS feed items asynchronously and load them into a meteor collection.
I have the feedparser npm module that does the parsing. It emits three events .on('error'), .on('meta') and .on('readable) with three different outputs.
When I run it in fixtures.js, with just console.log statements to run the output, its working fine.
When I use the same code to insert into a collection, I get errors related to asynchronocity of the function (assuming something to do with fibers)
So, I want to make it into a meteor method using futures as below -
http://www.discovermeteor.com/patterns/5828399
I tried but could not wrap my head around handling multiple events in Futures.
If you just want to push something to db at one point, it's enough to synchronize this call. Other than that, you can do whatever you want asynchronously. For example:
var Fiber = Npm.require('fibers');
var item = {};
var onInit = function() {
// do whatever with item
};
var onData = function() {
// do whatever with item
};
var onFinish = function() {
new Fiber(function(){
Documents.insert(item);
}).run();
};
Although Meteor is a great tool, I think node and its async insight is brilliant, and the best tool for what you are doing. Keep as a plan b having this part of your project be a straight node app.
Otherwise,
async from meteor
and
To handle events send to socket in more orginased way, I've made a router. In that router I want to assign each module to specific event. I've assigned event strings and its handlers to "handlers" object. Then I wanted to assign listeners to given socket in a loop. After assignment I listed all events and it handlers in given socket to be clear. Everything seemd to be fine. Unfortunently, it doesnt work. Socket acts like it would assign every event in handlers object to first handler in that object. Handmade verion works fine, but I just cant get it why simple loop fails :/
Here is code of the socketio handling socket by router:
var socketOptions = {transports:['flashsocket', 'websocket', 'htmlfile', 'xhr-polling', 'jsonp-polling']};
var io = socketio.listen(server,socketOptions).on('connection', function (socket) {
streamRouter(io,socket);
});
And here is code of the router. I've wrote how looks handmade version of assigning sockets and how did look like looped verion. Normally, second one is commented.
var handlers = {
"message": require("./message").doAction,
"subscribe": require("./subscribe").doAction
}
exports.handleConnection = function(io,socket) {
//handmade version
socket.on("subscribe", function(msg){
require("./subscribe").doAction(io,socket,msg);
});
socket.on("message", function(msg){
require("./message").doAction(io,socket,msg);
});
//loop version
for( var event in handlers ) {
socket.on(event, function(msg){
handlers[event](io,socket,msg);
});
}
}
I'ld be greatful for any advice where the bug lies. In short time I'll have many handlers and assigning them one by one will be an ugly copying-pasting through many lines of code :/
In your for-in loop, you're constructing functions that are all closed over the same event variable. As a result, when those functions get executed, they will all refer to the same value. Additionally, you're not guarding your for-in loop against prototype members (which may or may not be intended).
Instead, try this:
Object.keys(handlers).forEach(function(event){
socket.on(event, function(msg){
handlers[event](io, socket, msg);
});
});
For your loop to work, you need to create a new scope for each handler:
for( var event in handlers ) {
(function(handler) {
socket.on(event, function(msg){
handler(io,socket,msg);
});
})(handlers[event]);
}
This has to do with scoping: Javascript doesn't create a 'new' event variable for each loop, and by the time the event handler is called, event will be overwritten (and will contain the value it had in the last iteration of the loop).
This page offers more explaination.