JavaScript, node.js wait for socket.on response before continuing - javascript

I need to get information from the server on the client side.
So on the server side I got this when a client first connect:
socket.on('adduser', function(username){
// misc code, where i set num_player and whatnot
socket.emit('confirmauth', socket.id, socket.num_player, function(data){
console.log(data)
});
// code
}
and on the client side I got this:
var current_player;
socket.on('confirmauth', function(id, username, num, callback) {
current_player = new Player(username,id, num);
console.log(current_player.id); // works
console.log(current_player.num); //works
callback('ok i got it');
});
console.log(current_player.id); //undefined
console.log(current_player.num); //undefined
my problem is that outside of the socket on, the player is not defined. It seems that javascript doesn't wait for my socket on to retrieve data before carrying on.
I tried to wrap socket.on in a $.when done, but it doesn't work. I tried to do a callback, but I think I may not have understood very well how it is supposed to work. So if one of you is willing to help me, I will be grateful
Thank you for your answers.

If you are putting the current_player variable outside of the on callback in an attempt to return it, the alternative is to make your own function receive a callback
function getPlayer(onDone){
socket.on('confirmauth', function(id, username, num, callback) {
var current_player = new Player(username,id, num);
onDone(current_player);
});
}
And instead of doing
var player = getPlayer();
//...
You do
getPlayer(function(player){
//...
});
It kind of sucks that the "callbackyness" is a bit infectious in Javascript but such is life until everyone starts using Generators instead.

This is since socket.on runs taking a callback, and is in the callback where the player is set. however, after calling socket.on you try to read the player, but it is not set, since the callback was not called. Remember you're dealing with asynchronous programming. Try using nibble to chain that code after the callback.
Remember: socket.on and many other socket handlers -and even many other event handles (e.g. jquery)- DON'T wait to the event to happen, so you must think asynchronously.
Take this example:
You call socket.on while the socket is connecting (The connection takes 3 seconds since, e.g., the client is behind Tor). socket.on assigns the event handler (it's the only thing it does since it works for the 11 or more events it has, in the same way).
You access the player.
But the point 1 does not take 3 seconds since it's just an assignment - the connection is what takes 3 seconds to be established. In that sense, you have an undefined (actually: unassigned) variable.

All of these operations are asynchronous, so it's best to avoid global variables and statefulness as much as possible.
That said, have your callback take the newly created Player object to where it needs to go - a register of players, maybe? - and transport it that way.
callback(current_player)
From there, you can put it into a globally available object as you like, for instance if you're using Backbone or have something on the server side keeping track of current users.

Related

Socket is called multiple times

I read other questions about this issue but didn't find a solution for the problem. I saw that you can removeListeners or move the socket code out of the click handler for example but it doesn't seem to fit my problem.
I am using sockets + ajax. My code logic goes as follows:
//client side
button.onclick = function () {Communicate(info);return false;};
function Communicate(info) {
// ajax call
}
// server side -> receive ajax
app.post("/...",function(req,res) {
// do stuff (...)
// send global socket call
io.sockets.emit("GlobalCall",xx);
// return ajax call to the user who triggered this
res.send(...));
});
// client side again
socket.on("GlobalCall",function(x)) {
// this is called twice sometimes for some reason... It should be called only once each time the server emits ...
}
As I commented on the code, the server emits only once but "GlobalCall" happens twice sometimes.
Any help? Thank you
The solution wasn't really related to sockets. What was happening had to do with jquery animations that were executed twice for each assigned property. I didn't know that and didn't notice it. Wrong to blame the sockets.

Calling socket.disconnect in a forEach loop doesn't actually call disconnect on all sockets

I am new to javascript world. Recently I was working on a chat application in nodejs. So I have a method called gracefulshutdown as follows.
var gracefulShutdown = function() {
logger.info("Received kill signal, shutting down gracefully.");
server.close();
logger.info('Disconnecting all the socket.io clients');
if (Object.keys(io.sockets.sockets).length == 0) process.exit();
var _map = io.sockets.sockets,
_socket;
for (var _k in _map) {
if (_map.hasOwnProperty(_k)) {
_socket = _map[_k];
_socket.disconnect(true);
}
}
...code here...
setTimeout(function() {
logger.error("Could not close connections in time, shutting down");
process.exit();
}, 10 * 1000);
}
Here is what is happening in the disconnect listener.The removeDisconnectedClient method simply updates an entry in the db to indicate the removed client.
socket.on('disconnect', function() {
removeDisconnectedClient(socket);
});
So in this case the disconnect event wasn't fired for all sockets. It was fired for only a few sockets randomly from the array. Although I was able to fix it using setTimeout(fn, 0) with the help of a teammate.
I read about it online and understood only this much that setTimeout defers the execution of of code by adding it to end of event queue. I read about javascript context, call stack, event loop. But I couldn't put together all of it in this context. I really don't understand why and how this issue occurred. Could someone explain it in detail. And what is the best way to solve or avoid them.
It is hard to say for sure without a little more context about the rest of the code in gracefulShutdown but I'm surprised it is disconnecting any of the sockets at all:
_socket = _map[ _k ];
socket.disconnect(true);
It appears that you are assigning an item from _map to the variable _socket but then calling disconnect on socket, which is a different variable. I'm guessing it is a typo and you meant to call disconnect on _socket?
Some of the sockets might be disconnecting for other reasons and the appearance that your loop is disconnecting some but not all the sockets is probably just coincidence.
As far as I can tell from the code you posted, socket should be undefined and you should be getting errors about trying to call the disconnect method on undefined.
From the method name where you use it I can suppose that application exits after attempts to disconnect all sockets. The nature of socket communication is asynchronous, so given you have a decent amount of items in _map it can occur that not all messages with disconnect will be sent before the process exits.
You can increase chances by calling exit after some timeout after disconnecting all sockets. However, why would you manually disconnect? On connection interruption remote sockets will automatically get disconnected...
UPDATE
Socket.io for Node.js doesn't have a callback to know for sure that packet with disconnect command was sent. At least in v0.9. I've debugged that and came to conclusion that without modification of sources it is not possible to catch that moment.
In file "socket.io\lib\transports\websocket\hybi-16.js" a method write is called to send the disconnect packet
WebSocket.prototype.write = function (data) {
...
this.socket.write(buf, 'binary');
...
}
Whereas socket.write is defined in Node.js core transport "nodejs-{your-node-version}-src\core-modules-sources\lib\net.js" as
Socket.prototype.write = function(chunk, encoding, cb)
//cb is a callback to be called on writeRequest complete
However as you see this callback is not provided, so socket.io will not know about the packet having been sent.
At the same time when disconnect() is called for websocket, member disconnected is set to true, and "disconnect" event is broadcasted, indeed. But synchronously. So .on('disconnect' handler on server socket doesn't give and valuable information about whether the packet was sent or not.
Solution
I can make a general conclusion from this. If it is so critical to make sure that all clients are immediately informed (and not wait for a heartbeat timeout or if heartbeat is disabled) then this logic should be implemented manually.
You can send an ordinary message which will mean for the client that server is shutting down and call socket disconnect as soon as the message is received. At the same time server will be able to accept all acknowledgements
Server-side:
var sockets = [];
for (var _k in _map) {
if (_map.hasOwnProperty(_k)) {
sockets.push(_map[_k]);
}
}
sockets.map(function (socket) {
socket.emit('shutdown', function () {
socket.isShutdown = true;
var all = sockets.every(function (skt) {
return skt.isShutdown;
});
if (all) {
//wrap in timeout to let current tick finish before quitting
setTimeout(function () {
process.exit();
});
}
})
})
Clients should behave simply
socket.on('shutdown', function () {
socket.disconnect();
});
Thus we make sure each client has explicitly disconnected. We don't care about server. It will be shutdown shortly.
In the example code it looks like io.sockets.sockets is an Object, however, at least in the library version I am using, it is a mutable array which the socket.io library is free to modify each time you are removing a socket with disconnect(true).
Thus, when you call disconnect(true); if the currently iterated item from index i is removed, this effect like this happens:
var a = [1,2,3,4];
for( var i in a) {
a.splice(i,1); // remove item from array
alert(i);
}
// alerts 0,1
Thus, the disconnect(true) call will ask the socket.io to remove the item from the array - and because you are both holding reference to the same array, the contents of the array are modified during the loop.
The solution is to create a copy of the _map with slice() before the loop:
var _map = io.sockets.sockets.slice(); // copy of the original
It would create a copy of the original array and thus should go through all the items in the array.
The reason why calling setTimeout() would also work is that it would defer the removal of the items from the array, allowing the whole loop iterate without modifying the sockets -Array.
The problem here is that sockjs and socket.io use asynchronous "disconnect" methods. IE. When you call disconnect, it is not immediately terminated. It is just a promise that it WILL be terminated. This has the following effect (assuming 3 sockets)
Your for loop grabs the first socket
The disconnect method is called on the first socket
Your for loop grabs the second socket
The disconnect method is called on the second socket
The disconnect method on the first socket finishes
Your for loop grabs the third socket
The disconnect method is called on the third socket
Program kills itself
Notice, that sockets 2 and 3 haven't necessarily finished yet. This could be for a number of reasons.
Finally, setTimeout(fn, 0) is, as you said, blocking the final call, but it may not be consistent (I haven't dug into this too much). By that I mean, you've set the final termination to be AFTER all your sockets have disconnected. The setTimeout and setInterval methods essentially act more like a queue. Your position in the queue is dictated by the timer you set. Two intervals set for 10s each, where they both run synchronously will cause one to run AFTER the other.
After Socket.io 1.0, the library does not expose you an array of the connected sockets. You can check that io.socket.sockets.length, is not equal to the open socket objects. Your best bet is that you broadcast a 'disconnect' message to all the clients that you want to off, and on.'disconnect' on the client side close the actual WebSocket.

What is the proper method to reuse handlers/callbacks in Socket.io events, and does it even matter?

I am trying to factor out the anonymous callback functions from my socket.on() events for two reasons:
It makes the code slightly easier to read and document.
I believe it can be more memory efficient.
Since the first reason is more of a personal preference, I will not address it--although I am definitely interested in seeing style guides and/or general recommendations.
The second reason is because the only way I have seen socket.io used is like this:
var io = require('socketio');
io.on('connection', function(socket) {
socket.on('event', function(data) {
// logic goes here
socket.emit('different event', differentData);
});
});
This can work quite well, but I believe the anonymous functions are instantiated for each and every incoming connection.
I would factor this out through the following:
io.on('connection', function(socket) {
socket.on('event', eventHandler);
function eventHandler(data) {
// logic goes here
socket.emit('different event', differentData);
});
});
Still, that appears to still create an instance of 'eventHandler' for every new connection.
My current attempt at factoring this out looks like this:
var sockets = {}
io.on('connection', function(socket) {
socket.on('login', login.bind(socket));
socket.on('get information', getInformation);
});
function login(data) {
// logic goes here
// Save the socket to a persistent object
sockets[data.userId] = this;
this.emit('logged in', loginData);
});
function getInformation(data) {
// authentication, db access logic goes here
// Avoid binding by using previously stored socket
sockets[data.userId].emit('got information', infoData);
});
This works, but I am believe that bind() just creates another function, anyway, subverting any real benefit of not including the login callback in the connection callback.
As for the other handlers, it seems that a persistent object is necessary, as evidenced by EtherPad's method (SocketIORouter.js:62). Since I plan to be listening to a sizable number of events, it seems like pulling the callbacks out of the connection callback is the best way to go about it. Maybe bind will create a new function each time it is called, but in this case, it will only be called twice per connection rather than 10-20 times. I imagine that for thousands of clients, that would make a difference, but I am not sure.
So, to sum up, is this a proper approach, or is there perhaps a better one that I should be using? Or, is this just another case of premature optimization?

Javascript Async.js Parallel not working with socket.io

I have the following block of code:
async.parallel([
function(cb){ module.rpc("user.data",{"username":data.username},cb); },
function(cb){ module.rpc("group.list",{"username":data.username},cb); },
function(cb){ module.rpc("set.list",{},cb); }
],function(error,result){
if(error){ callback(error); return; }
var user = result[0], groups = result[1], sets = result[2];
callback(null,template.render({"user":user,"groups":groups,"sets":sets}));
});
module.rpc is a function that fetches the necessary data from the server via socket.io. Now, the final function (async.parallel's second argument) is supposed to be called only after the given 3 functions have called-back. However, during the template (EJS) rendering, when I try to access groups.data, I sometimes get the error:
Uncaught TypeError: Cannot read property 'data' of undefined
The code seems perfectly fine to me, but works only occasionally. I have reloaded the page repeatedly, without changing the underlying code, and had a success rate of about 20%. I have absolutely no clue why things are going wrong here. All I can guess is that the assignment of that variable is delayed. And so I tried delaying the rendering using window.setTimeout, but to no avail. Why is this happening? How do I fix it?
socket.io keeps sending events to the server repeatedly until it gets an acknowledgement. Depending on the server load, acknowledgements might not be immediate. So multiple identical requests were being sent, and since async.parallel uses a counter instead of individually tracking each function, the final function was getting prematurely called.

How to develop node.js run-time strategy?

Node.js approach is event driven and I was wondering how would you tackle the problem of when to fire off an event?
Lets say that we have some actions on a web application: create some data, serve pages, receive data etc.
How would you lay out these events? In a threaded system the design is rather "simple". You dedicated threads to specific set of tasks and you go down the road of thread synchronization. While these task are at low on demand the threads sit idle and do nothing. When they are needed they run their code. While this road has issues it's well documented and kind of solved.
I find it hard to wrap my head around the node.js event way of doing things.
I have 10 request coming in, but I haven't created any data so I can't serve anying, creating data is a long action and another 5 client wants to send data. What now?
I've created the following untested code which is basically a pile of callbacks which get registered and should be executed. There will be some kind of a pile manager that will run and decide which code does it want to execute now. All the callback created by that callback can be added "naturally" to the even loop. It should also register it's self so the event loop could give the control back to it. Other things like static content and what ever can be bound differently.
How can I register a call back to be the last call in the current event loop state?
Is this a good way to solve this issue?
The most important thing to remember when coming from a threaded environment is that in node you don't wait for an action to finish happening, instead you tell it what to do when it is done. To do this you use a callback, this is a variable which contains a function to execute, or a pointer to a function if you like.
For example:
app.get('/details/:id?', function (req, res) {
var id = req.params.ucid,
publish = function (data) {
res.send(data);
};
service.getDetails(id, publish);
});
You can then invoke the publish method from within your get details method once you have created the required data.
getDetail : function (id, callback) {
var data = makeMyData(id);
callback(data)
}
Which will then publish your data back to the response object. Because of the event loop node will continue to serve requests to this url without interrupting the data generation from the first request
The answer chosen is the most correct, there is but one minor code change and that is:
Change this function from this:
getDetail : function (id, callback) {
var data = makeMyData(id);
callback(data)
}
To that:
getDetail : function (id, callback) {
var data = makeMyData(id);
setTimeout(callback, 0, data);
}
Update 2019:
In order to comply with community standard I've broken off an update to a new answer.
I've used setTimeout because I wanted to defer the callback to the back of the event loop. Another option I've used was process.nextTick(), this helped to defer the callback to the end of the current event processed.
For example:
getDetail : function (id, callback) {
var data = makeMyData(id);
process.nextTick(((info)=> callback(info))(data))
}

Categories