Jump to content

[SOLVED] NodeJS Strange Variable Scope


MrFish

Recommended Posts

I really don't know how to describe this problem and I'm not sure if it happens with js through a browser or just Node.

 

So I'm using socket.io to communicate with an app on PhoneGap. I'm writing a simple communication manager to listen for specific events and pass the information to the appropriate class to be handled.

 

I thought the cleanest way to map the event to the function in the communication manager (to then decide where it goes and how to pass it) was create a key-value pair like this-

exports.eventMap = {	"disconnect":"handleDisconnect",	"login_request":"loginRequest",}

When a connection is made the socket is sent to the handleConnection method. Like this-

	var that = this;	this.socket.on("connection", function(socket)	{		that.handleConnection(socket);	});

All's good- nothing wrong so far. Now this method will record the socket by it's ID and loop through the event map to assign callbacks with a simple for-in loop

exports.handleConnection = function(socket){	var socketID = socket.id;	this.socketMap[socketID] = socket;	console.log("connection to " + socketID + " made");	var that = this;	for(var _event in this.eventMap)	{		var callback = that.eventMap[_event];		var func = function(data)		{			that[callback](socket, data);		}		socket.on(_event, func);	}}// Eventsexports.handleDisconnect = function(socket, data){	var socketID = socket.id;	console.log("connection to " + socketID + " broke");}exports.loginRequest = function(){	console.log("how did this happen??");}

The two events get mapped but to the last function in the eventMap object. So "disconnect" and "login_request" both call loginRequest.

 

I have got a round-about way of fixing this but of course I want to know why this happens. Doesn't make sense to me.

exports.handleConnection = function(socket){	var socketID = socket.id;	this.socketMap[socketID] = socket;	console.log("connection to " + socketID + " made");	var that = this;	for(var _event in this.eventMap)	{		var rand = "_" + Math.round(Math.random() * 1000000);		console.log(rand);		eval("var " + rand + " = that.eventMap[_event]");		eval("var func = function(data){that["+rand+"](socket, data);}");		socket.on(_event, func);	}}
Edited by MrFish
Link to comment
Share on other sites

The reason why that happens is because by the time that event handler runs, the variables are set to something other than they were when you were going through the loop. You have this loop:

 

 

 for(var _event in this.eventMap){  var callback = that.eventMap[_event];  var func = function(data)  {   that[callback](socket, data);  }   socket.on(_event, func);}

 

When the function func gets executed, it runs the code inside the function which executes that[callback]. But the callback variable is not set to what it was when that function was defined, instead it uses the current value of callback (which will be whatever the last value was, unless it got changed after that). So it's not using the value that the variable had when the function got defined, it uses the value that the variable currently has when the function gets executed, which is going to be the value that it was set to the last time through the loop. Hopefully that makes sense. People often use closures to work around that, where you define an anonymous function and pass it the values (the current values) that you want the function to use. e.g.:

 

 

(function (ev, cb, sck, sc) {  sck.on(ev, function (data) {    sc[cb](sck, data);  }); })(_event, callback, socket, that);
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...