Reputation: 32354
I'll be quick and jump straight to the case. The code is commented so you know my intentions. Basically, I'm building a small, HTML5 based game, and opposed to saving stuff on the server or in a cookie, I'll just provide the player a level code. When a player enters the code (in a form of a simple hash) into a text input field, and clicks a button to load that level, the function "l" is called. That function first retrieves the player's entries, then iterates through the list of hashes and compares them. When a match is fond, that certain level is supposed to be loaded, but there were errors. I did a little bit of debugging, and I found out that the value of the iterator ("i"), has changed inside a setTimeout! I want to pause 1 second, because loading the level immediately would be simply too quick and would look bad.
levelCodes = //Just a set of "hashes" that the player can enter to load a certain level. For now, only "code" matters.
[
{"code": "#tc454", "l": 0},
{"code": "#tc723", "l": 1},
]
var l = function() //This function is called when a button is pressed on the page
{
var toLoad = document.getElementById("lc").value; //This can be "#tc723", for example
for (i = 0; i < levelCodes.length; i++) //levelCodes.length == 2, so this should run 2 times, and in the last time i should be 1
if (levelCodes[i].code == toLoad) //If I put "#tc723" this will be true when i == 1, and this happens
{
console.log(i); //This says 1
setTimeout(function(){console.log(i)}, 1000); //This one says 2!
}
}
Upvotes: 8
Views: 1775
Reputation: 1
You can pass the third parameter which is about to change after the timeout duration and that will be your first argument of the function. eg.
setTimeinterval(function(x){console.log(x)},1000,i)
Upvotes: 0
Reputation: 9361
By the time setTimeout callback gets to execute variable i will have a value of 2, because you don't quit the loop and i keeps counting until it's equal to levelCodes.legnth (which is 2). Basically you need to add return or break after calling setTimeout. Also, you are not declaring variable i, so it will be bound to global namespace, which is bad and may potentially lead to very obscure bugs. For example, the value of i can be changed in other functions so setTimeout callback will see a different value. You need to add var i; in the beginning of function l.
Upvotes: 1
Reputation: 198334
The others already wrote the reason for the behaviour you're getting. Now the solution: change the setTimeout
line to:
(function(i) {
setTimeout(function(){console.log(i)}, 1000);
})(i);
This works because it captures the current value of the variable i
into yet another closure, and the variable within that closure doesn't change.
Upvotes: 7
Reputation: 236022
ECMAscript uses the technique of lexical closures
which is nothing else than an internal storage to all parent context objects / lexical environment records (ES3 / ES5).
In short words, your anonymous function used by setTimeout
closes over that i
variable, so while that timeout is "waiting", your loop continues. setTimeout
operatores asynronously of course and that in turn means, at the time that loop has finished the value if i
is of course 2.
Now remember about the closure stuff, our anonymous function in setTimeout
still holds a reference to i
when it finally fires (after 1000ms). So it correctly shows you the value of 2.
If you want to show the numbers for each iteration after 1000ms, you need to invoke another context. This might look similar to
setTimeout((function( local ) {
return function() {
console.log( local );
};
}( i )), 1000);
Upvotes: 4
Reputation: 35409
The for loop continuously increments i
until the loop condition is met, even though the code in the for loop does not execute, when the code in the setTimeout
executes it shows the current value of i
- which is 2
.
Upvotes: 3