Reputation: 9969
function updateRoomsList() {
//empty the rooms list
$("#rooms").empty();
//fetch the non-joined rooms, id and name
$.get('JoinPart', {
goal: 5,
userName: $("#userName").html()
}, function (responseText) {
var i = 0;
id = "";
while (i < responseText.length) {
if (responseText.charAt(i) == '.') {
//Now we got the full Id of a room, lets add it
$.get('JoinPart', {
goal: 6,
roomId: id
}, function (responseText) {
roomName = responseText;
$("#rooms").append('<div id="room' + id + '" class="listItem"><span title="Join Room" class="joinButton">+</span><div class="listItemContent">' + roomName + '</div></div>');
});
id = "";
} else {
id = id + responseText.charAt(i);
}
i++;
}
});
}
In this Function there's a variable id
, if I alert(id);
after
if(responseText.charAt(i)=='.')
I get the proper value of the id calculated in the else
, but when I do alert(id);
inside the $.get
the id is empty ""
, means in the append
function, the id
has no value, how can I get this id to have the value outside of the $.get function ?
Upvotes: 1
Views: 132
Reputation: 20885
That's because id
is scoped in the outer closure, so your inner callback gets a reference to the last value it's set at the end of the while
loop. If you need a private copy of the id
variable inside your second $.get()
callback, you need to write
$.get('JoinPart', {goal :6, roomId :id }, (function (id) {
return function (responseText) {
roomName = responseText;
$("#rooms").append('<div>' + id + '</div>');
}
})(id));
When you do this, you create a new scope with a local variable id
which is given the exact value id
had at the current iteration, and not the local variable id
itself, which at the end of the loop (or really just at the next iteration) could be anything.
The construct (function(arg){})(arg)
is called an immediately invoked function expression. Please note that this function returns a new function object, the one required by $.get()
A closure is a function defined inside another one. A closure, by definition, can access all variables local to the outer function. When you supply a callback for later execution to $.get()
, you are in fact using a closure: when the callback will be executed (possibly after several seconds) the JS engine will make id
still accessible, but its value obviously depends on the rest of the code.
If you need to store the current snapshot of a variable for later reading, you need to create a new closure (ie a new scope): we use the IIFE for this purpose. The IIFE is executed right before the actual HTTP request is fired (ie before the $.get()
is even called), and it's the nearest scope for the callback, so it will be used to resolve the id
reference when the callback will be executed.
You can test if you understand the concept by figuring out what the following snippet will print
function inner() {
console.log(i);
}
for (var i = 0; i < 3; i++) {
inner();
setTimeout(inner, 1000);
}
Upvotes: 1
Reputation: 60848
Code flow is not the order you think.
id="";
Is happening before the function-within-a-function, which is called a closure, by the way (this should help in your googling). Add the print statements
alert("here 1");
alert("here 2");
in the closure, and before the id="";
and this should jump out.
Easiest fix is probably to swap id
with roomId
in the closure, since, being a simple string or int, the value is fully copied to roomId
, where it will be safe even if id
changes (ok, more accurately, id=...
only changes the reference so the value referred to by roomId
stays the same.)
Upvotes: 1