corazza
corazza

Reputation: 32354

setTimeout appears to be changing my variables! Why?

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

Answers (5)

aman singh
aman singh

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

Roman Bataev
Roman Bataev

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

Amadan
Amadan

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

jAndy
jAndy

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

Alex
Alex

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

Related Questions