socket.io get socket inside a callback - javascript

I'm using socket.io for node.js. If you add an event handler like this:
io = require('socket.io')(http);
io.on('connect',function(socket){
socket.on('some event',function(arg1,arg2){
/// using socket to emit events for example
}
}
Then I can access socket inside the callback for the 'some event'
However what if I use it like this
io = require('socket.io')(http);
io.on('connect',function){
socket.on('some event',myfunction);
}
function myFunction(arg1,arg2)
{
//I want to use calling socket here.
}
How do I access socket in the later case?
I need the socket to get the socket.id so I can know who called this event.
Thanks

Well, if I understand what you want to do, you can simply:
io = require('socket.io')(http);
io.on('connect',function){
socket.on('some event',myfunction);
}
function myFunction(arg1,arg2)
{
var socketId = this.id; //here you go, you can refer the socket with "this"
}
This is what I usually do to keep the code clean:
var on_potato = function(potatoData){
var socket = this;
var id = socket.id;
//use potatoData and socket here
};
var on_tomato = function(tomatoData){
var socket = this;
var id = socket.id;
//use tomatoData and socket here
};
var handleClientConnection = function (client) {
client.on('potato', on_potato);
client.on('tomato', on_tomato);
};
io.on('connection', handleClientConnection)

Alright, so after discussing this a potential solution is to just invoke your named function from within the anoymous callback function passed to the on method.
io.on('connect', function(socket){
socket.on('someEvent', function(username, date){
// If you emitted an object, you'll need to parse the incoming data. So say
// You emitted {username: 'SomeName', date: 'SomeDate' }
// You could just pass data.username and data.date directly
// or put them into local variables, like:
//var username = data.username, date = data.date;
// Invoke your named function here and you can pass
// whatever you want to it, along with the socket
myFunction(username, date, socket)
})
})
myFunction(username, date, socket){
// Do whatever you're doing with the passed paramaters
}

I've used Lodash's partial function quite often to solve problems like this (Underscore has one as well, which does the same thing). Basically what it does is create a new function that has some of the original function's arguments filled in. So what you would do is something like this:
io = require('socket.io')(http);
io.on('connect', function(socket) {
socket.on('some event', _.partial(myfunction, socket));
});
function myFunction(socket, ...args) {
// whatever you wanna do
}
Then, when the new curried function is returned from the partial execution, it has socket prefilled as the first param and you can use it how you'd like.
Just a note that the ...args is just a placeholder for whatever you want to put there. Also, I'm not sure if socket.io passes anything into the function on firing the callback, which may affect the placement of the arguments into the curried function. If socket, shouldn't be the first argument, you can make it the second thusly:
io = require('socket.io')(http);
io.on('connect', function(socket)){
socket.on('some event', _.partial(myfunction, _, socket));
}
function myFunction(arg1, socket, ...args) {
// whatever you wanna do
}

Related

socket.io callback function not working

I used to use socket.io emit callback like following:
client:
mysocket.emit('helloword', 'helloword', function(param){
console.log(param);
});
server:
var server = require('http').Server(app);
var sserver = io(server);
sserver.on('connection', function(socket){
console.log('connection');
socket.on('helloword', function(message, callback){
console.log(message);
console.log(callback+'');
console.log('arguments', arguments);
callback('helloword');
})
});
server.listen(config.port);
I'm using angular-socket-io as a wrapper for socket.io-client. My service is simple as:
'use strict';
angular.module('core').factory('mysocket', function(socketFactory){
return socketFactory();
});
server output:
connection
helloword
function (){
// prevent double callbacks
if (sent) return;
var args = Array.prototype.slice.call(arguments);
debug('sending ack %j', args);
var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK;
self.packet({
id: id,
type: type,
data: args
});
}
arguments { '0': 'helloword', '1': [Function] }
My client console:
My question:
Why my callback is not firing ?
The function socket.on(event, callback) will listen for an event from the client and then run the callback when the event is triggered. In your case, when it hears 'helloword' it will run the callback function that you defined as the second parameter: function(message, callback). Your server log output shows that, actually, all of the console.log calls in your callback are being run.
To see what's going on client side try set the localStorage.debug = '*'
See this for more info: http://socket.io/docs/migrating-from-0-9/#log-differences (assuming you use socketio 1.0+)

I am receiving a message in node.js and the object socket is undefined

I have the following code:
function Socket(io, playGame, mapper) {
io.on('connection', function (socket) {
// message handler for the chat message
socket.on('sendChat', function (data) {
console.log(socket);
console.log(data);
console.log('recieved chat');
var connectedPlayer = playGame.findConnectedPlayer(socket);
if (!connectedPlayer)
return;
var connectedGame = playGame.findConnectedGame(socket, connectedPlayer.gameId);
if (!connectedGame)
return;
// send update game with players properly ordered
for (socketIndex in this.sockets) {
var socket = this.sockets[socketIndex];
// send the new data to each player
socket.socket.emit('chatUpdate', { chatText: data.chat });
}
});
// message handler for join game message
socket.on('joinGame', function (data) {
console.log('recieved join:', JSON.stringify(data));
if (!playGame.newConnectedPlayer(socket, data))
return;
...
In the method for sendChat, socket is undefined. In the method for joinGame, socket is defined. I have tried several ideas, but the problem persists. Any help would be appreciated.
You'll have to rename one of the 2 socket variables -- either the parameter for 'connection' or the var in the loop:
io.on('connection', function (socket) {
for (socketIndex in this.sockets) {
var socket = this.sockets[socketIndex];
The var is shadowing the parameter, rendering the parameter inaccessible.
This happens in part because the var socket doesn't only exist within the for loop. JavaScript vars are function-scoped and their declarations are hoisted to the top of the function, as in:
socket.on('sendChat', function (data) {
var connectedPlayer, connectedGame, socket; // each initially `undefined`
console.log(socket);
// ...
for (socketIndex in this.sockets) {
socket = this.sockets[socketIndex];
// ...
});
And, having the same exact name, at most only one of them can be reached from a particular function.
Also note that the for loop and var socket aren't really necessary.
You can use the Socket.IO Server's own .emit() method to send a message to all clients.
io.emit('chatUpdate', { chatText: data.chat });

JavaScript - objects liefetime

I'm trying to create a node.js server using socket.io. At the moment is just proof on concept.
I created 2 files, first for server and anther for server side user.
server-core.js
'use strict';
var io = require('socket.io').listen(4567);
var user = require('./server-user');
var users = [];
io.sockets.on('connection', function(socket){
var su = new user.ServerUser(socket);
users[socket.id] = su;
socket.on('auth', su.auth);
socket.on('disconnect', su.disconnect);
});
io.sockets.on('disconnect', function(socket){
console.log('disconnect');
users[socket.id].disconnect();
});
console.log('Server started');
server-user.js
var ServerUser = (function(){
function ServerUser(socket){
this.socket = socket;
console.log('serverUser-ctor ' + this.socket)
}
ServerUser.prototype.auth = function(data){
console.log('auth received\r\n' + data);
this.socket.emit('auth', {
Id: data.Id,
Timestamp: data.Timestamp,
StringField: data.StringField
});
}
ServerUser.prototype.disconnect = function(){
console.log('Client disconnected');
}
return ServerUser;
})();
module.exports = {
ServerUser: ServerUser
};
my C# client connects fine to server, but when user-server tries to send the answer back the this.socket is undefined in ServerUser.prototype.auth method. This tell me that the instance of the ServerUser that I create in server-core is not being hold and when 'auth' method is called a new instance of object is actually being created.
To proof this I replaced this line
socket.on('auth', su.auth);
with such one
socket.on('auth', function(data){su.auth(data);});
After this it worked as needed.
Is this the correct way to write JS code?
Is there a better way to separate logic under separate files and classes when writing large node.js applications?
Thx for any opinions.
The problem is the invocation context. When you pass su.auth to socket.on(), this no longer refers to su inside of auth. So, there are a couple of ways to fix that. Using an anonymous function, as you found, is one. Function.bind is another:
socket.on('auth', su.auth.bind(su));

Node.js module level variables are undefined

I'm having an issue accessing the scope of a module level variable from within a function inside of said module. See below...
var socketio = require('socket.io');
var socket = socketio.listen();
var myCustomModule = require('./lib/mycustommodule')('http://mysite:8080');
socket.on('connection', function connection(socket) {
socket.emit('message', {data:"test1"}); <====THIS WORKS
socket.on('init', function init(data) {
socket.emit('message', {data:"test1"}); <====THIS WORKS
refreshMyCustomModule();
});
});
var refreshMyCustomModule = function() {
socket.emit('message', {data:"test1"}); <=====THIS DOESN'T WORK
myCustomModule.beginSomeAsyncTask(function(data) { <======THIS DOESN'T WORK
socket.emit('message', {data:"test2"}); <========THIS DOESN'T WORK
});
};
Looking at the sample above. When I call my refreshMyCustomModule function suddenly socket and myCustomModule become undefined. I've also tried using this as well as setting up a var self = this.
I've written a bunch in javascript on the client but when coding in node.js it seems like scoping is different and I just can't crack this nut.
Note that the socket at the global level of your script and socket within your function connection are two different variables. The one inside your function connection is the argument that was passed into that function from the connection event. The one you're using in refreshMyCustomModule is the global one, the one on which you called listen.
This is clearer if we change their names, since they're different variables:
var socketio = require('socket.io');
var socketUsedForListen = socketio.listen();
var myCustomModule = require('./lib/mycustommodule')('http://mysite:8080');
socketUsedForListen.on('connection', function connection(socketFromConnection) {
socketFromConnection.emit('message', {data:"test1"});
socketFromConnection.on('init', function init(data) {
socketFromConnection.emit('message', {data:"test1"});
refreshMyCustomModule();
});
});
var refreshMyCustomModule = function() {
socketUsedForListen.emit('message', {data:"test1"});
myCustomModule.beginSomeAsyncTask(function(data) {
socketUsedForListen.emit('message', {data:"test2"});
});
};
I'm reasonably certain you meant to use socketFromConnection in refreshMyCustomModule, not socketUsedForListen. If so, either move the refreshMyCustomModule into your connection callback, or pass the socket into it as an argument.
It's because you're using an async event in socket.on('init') that leads to the scope issue. I believe if you pass in the parameters you want to use from the parent it will work. e.g.:
var socketio = require('socket.io');
var socket = socketio.listen();
var myCustomModule = require('./lib/mycustommodule')('http://mysite:8080');
socket.on('connection', function connection(socket) {
socket.emit('message', {data:"test1"}); <====THIS WORKS
socket.on('init', function init(data) {
socket.emit('message', {data:"test1"}); <====THIS WORKS
refreshMyCustomModule(socket, myCustomModule);
});
});
var refreshMyCustomModule = function(socket, module) {
socket.emit('message', {data:"test1"}); <=====THIS DOESN'T WORK
module.beginSomeAsyncTask(function(data) { <======THIS DOESN'T WORK
socket.emit('message', {data:"test2"}); <========THIS DOESN'T WORK
});
};

How to run script and pass arguments if client is connected over socket?

Is it possible to run some script and if client connected pass arguments to it, something like this:
var io = require('socket.io').listen(http);
io.sockets.on('connection', function (client) {
console.log('Client connected');
var notifikacija = function (array) {
client.emit('populate', array);
}
});
///////////////////////////////////////////////////////////////////////
setInterval(function(){
var array = newArray();
array[0]='test';
notifikacija(array);
}, 2000);
Now it shows error: notifikacija is not defined. It is quite a strugle...
The notifikacija function is local to the scope of the io.sockets.on handler. You want it to be global so that you can access it in setInterval:
var notifikacija = function(){}; // just an empty function, in case it gets called before it has something to do
var io = require('socket.io').listen(http);
io.sockets.on('connection', function(client) {
console.log('Client connected');
notifikacija = function(array){ // once the client is available assign the function
client.emit('populate', array);
}
});
setInterval(function(){
var array = newArray();
array[0]='test';
notifikacija(array);
}, 2000);
Here's a blog post with some more information on scope in Javascript.

Categories