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.
Related
I have an event on server that is triggered when the client sends a message:
socket.on('message', function(data){
message.push(data);
});
And now when all clients send a message, I want to wait for 3 seconds and then call a function.
I tried with Interval:
var index=0;
var timer;
socket.on('message', function(data){
message.push(data);
if (index==0) //only once
timer = setInterval(call(), 3000);
index++;
});
function call(){
io.sockets.emit("confirmation", {status: "All messages are delivered"});
clearInterval(timer); // Clear interval
}
The problem is that the function call() call immediately and not after 3 seconds.
The second problem is that clearInterval(timer) in function call, does not work because the function call repeats.
If you only want this to occur once, you should use setTimeout instead of setInterval.
This will save you from needing to clear the interval later, and makes the code more explicit that you want it to happen exactly once, 3 seconds from "now."
In addition "(...)" at the end of a function name causes it to be called immediately. You want to pass the function "call" as a parameter, not cause it to be called immediately.
I would also recommend you name the function something other than call, as this is already a name of a standard function in Javascript, and therefore can get confusing quickly. Even calling it callback would reduce confusion.
So, what you're looking for is something like this:
setTimeout(callback,3000);
in place of this
setInterval(call(), 3000);
You are making a call to the function from setInterval function.
It should betimer = setInterval(call, 3000); and not timer = setInterval(call(), 3000);
Thanks for all the answers. I solve the problem.
The problem was that I wanted to create a setInterval() within io.on("connection", function(socket){}, but it makes for each client separately. I did not want this.
Now I put before and it works:
var index=0;
var timer;
var check=false;
timer = setInterval(function(){fcall()}, 3000);
function fcall(){
if(check)
{
io.sockets.emit("confirmation", {status: "All messages are delivered"});
clearInterval(timer); // Clear interval
timer=0;
}
}
io.on("connection", function(socket){
socket.on('message', function(data){
message.push(data);
if (index==0) //only once
check = true;
index++;
});
}
I have code to log every connection to my HTTP-server on a socket level and also log any incoming data.
This code was originally written for NodeJS 0.8 and works good there.
No my project is migrated to 0.10.24 and socket logging code stopped working.
Here is my code:
var netLogStream = fs.createWriteStream('net.log');
(function(f) {
net.Server.prototype.listen = function(port) {
var rv = f.apply(this, arguments); // (1)
rv.on('connection', function(socket) { // (2)
socket.on('data', function(data) {
data.toString().split('\n').forEach(function(line) { // (3)
netLogStream.write('... some logging here ... ' + line);
});
});
});
return rv;
};
})(net.Server.prototype.listen);
On 0.10 I can get to (1) and get Socket instance on (2) but I never get to (3). Same time my whole application works fine without any issues.
ADD: My server is created with Express#3.4.x
I'm not sure why the results are different between node v0.8 and v0.10, but if I had to guess, I'd be looking at the return value of net.Server.prototype.listen.
According to the documentation, this is an asynchronous method which emits the 'listen' event and invokes its callback when the listening is bound. You're not looking for that event, but rather, capturing the return value of listen, which for an async function, may not be well-defined. It's obviously not null or undefined since you don't get a runtime error, but the return value may not be the same between v0.8 and v0.10.
I honestly don't know for sure because I don't do low-level socket coding, but I have 2 suggestions to try:
Since the connection event is emitted from the Server object, perhaps you need this.on instead of rv.on.
Setup the connection event listener before you invoke listen just to minimize risk of race conditions.
Try this and see what happens:
var netLogStream = fs.createWriteStream('net.log');
(function(f) {
net.Server.prototype.listen = function(port) {
this.on('connection', function(socket) { // (2)
socket.on('data', function(data) {
data.toString().split('\n').forEach(function(line) { // (3)
netLogStream.write('... some logging here ... ' + line);
});
});
});
return f.apply(this, arguments); // (1)
};
})(net.Server.prototype.listen);
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.
I am following this tutorial on making HTML5 games. I wanted to try and mix node in to make it multiplayer. I am using node.js(v0.10.4) on server and crafty.js on front end.
I am using socket.io to send and receive messages. For now it's just me(not multiple clients). The weird thing that happens is that the message that comes from the server seems to be sent multiple times. I turned on debug mode in socket.io but it only seems to be sending the data once, yet on the front end the data seems to be coming in, in multiples. I set an incrementor on the data and it seems as if the incrementor is not incrementing multiple times but instead I am getting multiple copies of the same data.
here's node code:
var http = require('http').createServer(handler),
static = require('node-static'),
io = require('socket.io').listen(http);
io.set('log level', 3);
http.listen(80);
//attach the socket to our server
var file = new static.Server(); //Create a file object so we can server the files in the correct folder
function handler(req, res) {
req.addListener('end', function() {
file.serve(req, res);
}).resume();
}
io.sockets.on('connection', function (socket) { //listen for any sockets that will come from the client
socket.on('collected', function(data) {
/**** here's where the data is being sent back to the client *****/
socket.emit('messageFromServer', { data: data.number });
});
});
and here's front end code:
//messenger entity
Crafty.c('SendRecieveMessages',{
count: 0,
sendMessageToServer : function() {
console.log('got a village');
/**** Here's where we send the message to the server ****/
socket.emit('collected', { village : "The message went to the server and back. it's collected", number : this.count });
this.count++;
},
recieveMessageFromServer : function() {
socket.on('messageFromServer', function(data) {
/*** This data seems to be coming back or logging multiple times? ***/
console.log(data);
});
}
});
Lastly here's a screenshot of the debug in process. As you can see number is not always incrementing, it almost looks like the data is getting stored. Thanks!
http://cl.ly/image/0i3H0q2P1X0S
It looks like every time you call Crafty.c, recieveMessageFromServer() is getting called too. Every time recieveMessageFromServer is invoked, it attaches an additional event listener on the socket. That's why the first time data comes back you get one copy, then the second time you get two, the third time you get three, and so on.
You either need to prevent recieveMessageFromServer from being called multiple times, or use removeListener or removeAllListeners to remove the previously attached listeners.
Thanks to #Bret Copeland for helping me figure this one out. As he pointed out, every time socket.on() is called, it seems to add another listener. To prevent this...
I declared a global variable:
I declared a variable as a property in my Game object(in craftyjs, so use whatever you want in your setup)
Game = {
//lots of other code here...
//need this to use later for socket.io
send_message : true
}
then edited my recieveMessageFromServer() function to check whether its ok to send the message or not:
recieveMessageFromServer : function() {
console.log('does this show up multiple times?');
/* Check whether the send_message is true before sending */
if (Game.send_message) {
socket.on('messageFromServer', function(data) {
console.log(data);
Game.send_message = false;
});
}
}
So, I'd like to know how to create custom events in node.js, and I'm hitting a wall. I'm pretty sure I'm misunderstanding something about how express works and how node.js events work.
https://creativespace.nodejitsu.com That's the app.
When a user creates a new "activity" (something that will happen many times) they send a POST request. Then within my route, if that POST succeeds I'd like to emit an event, that tells socket.io to create a new namespace for that activity.
In my route file:
var eventEmitter = require('events').EventEmitter;
// Tell socket.io about the new space.
eventEmitter.emit('new activity', {activityId: body.id});
And socket.io:
// When someone creates a new activity
eventEmitter.on('new activity', function (data) { // this gives and error
var newActivity = '/activity?' + data.activityId;
io.of(newActivity).on('connection', function (socket) {
// Socket.io code for an activity
});
});
So the error I get is CANNOT CALL METHOD ON OF UNDEFINED and it refers to what would be line 2 in the socket.io above. I think I'm messing up my requires, maybe...or I'm not quite understanding how events work.
Any help, even a reference to good reading on Node.js events would rock!
Thanks!!!
If using express you can also just listen for event on the express 'app' which inherits from EventEmitter. For example:
res.app.on("myEvent", function)
and emit to it like
res.app.emit("myEvent", data)
You should treat EventEmitter as a class you can inherit from. Try this:
function MyEmitter () {
events.EventEmitter.call(this);
}
util.inherits(MyEmitter, events.EventEmitter);
Now you can use your class to listen and emit events:
var e = new MyEmitter;
e.on("test", function (m) { console.log(m); });
e.emit("test", "Hello World!");