3

I'm new to javascript and am trying to call a function using setTimeout from within a for loop. The loop executes for each member of a nodeList.

I'm finding that the function I'm calling with setTimeout is only actually executing during the last iteration of the loop. In the example below, I would like to make three separate calls to setTimeout but I'm finding that the first two calls are ignored.

function moveants(e, stepdistance) {

    . . . . .

    for(var i = 0; i < 3; i++)
    {
        var nextAnt = antgroup.childNodes[i]
        nextAnt.count = 0;
        nextAnt.member = i;
        setTimeout(function () { takeStep(nextAnt, mouseclickX, mouseclickY, 10) }, 0);
    }
}

function takeStep(ant, destX, destY, stepDistance) {

    . . . .

    . . . .

    if( condition )
    {
        return;
    }
    else
    {
        takeStep(ant, destX, destY, stepDistance);
    }
}

I have seen other posts that describe making multiple calls to setTimeout. Amazingly (to me), the multiple calls will work if I simply take them out of the for loop like this.

    setTimeout(function () { takeStep(antgroup.childNodes[0], 
         mouseclickX, mouseclickY, 10) }, 10);
    setTimeout(function () { takeStep(antgroup.childNodes[1], 
         mouseclickX, mouseclickY, 10) }, 10);
    setTimeout(function () { takeStep(antgroup.childNodes[2], 
         mouseclickX, mouseclickY, 10) }, 10);

I just can't figure out why there is a difference between calling them from within a for loop and calling them outside of one.

I am getting valid return values from the setInterval call in every case.. it's just that with only the last iteration of the for loop does the function actually execute.

Thanks in advance for any help.

Community
  • 1
  • 1
  • As Dark Slipstream says: you have 0 in the delay for a timeout Try it with a delay eg `setTimeout(function () { takeStep(nextAnt, mouseclickX, mouseclickY, 10) }, 10);` – James Khoury May 13 '11 at 02:24
  • I apologize. I was setting delay of 10 inside the loop as well but I accidentally deleted the time parameter in the post. – Michael Phillips May 13 '11 at 12:34

2 Answers2

8

nextAnt will be overwritten on every loop, so takeStep() will be called 3 times, but always with the same arguments.

You may try this instead:

(function(a,b,c){
     setTimeout(function(){
                           takeStep(a,b,c,10)}, 0);
      })(nextAnt, mouseclickX, mouseclickY);
Toby Allen
  • 10,997
  • 11
  • 73
  • 124
Dr.Molle
  • 116,463
  • 16
  • 195
  • 201
  • Wow. You're good. This works. Is this a _closures_ thing? I would have thought that the parameters passed to each iteration of setTimeout would inherently be private, but obviously that is not the case. I guess my question now is why are they private when I make the calls outside of the _for_ loop? I will have to ponder this for a while. Is it my understanding of variable scope within a _for_ loop that is incorrect or my understanding of how setTimeout works? Thanks a ton. – Michael Phillips May 13 '11 at 12:54
  • 2
    Ohhhh.. I think I get it now! So, so the _i_ in all three calls to setTimeout is actually still "in scope" the entire time the for-loop is iterating. I found this [other post](http://www.sitepoint.com/forums/javascript-15/settimeout-loop-closures-561807.html#post3900542) that discusses the same problem. Wild. Strange that variable scope can reach down through one layer of closure but not two. Anyway, very cool. Thanks again! – Michael Phillips May 13 '11 at 16:57
0

If your not setting a delay, then why bother with setTimeout?

In the loop your delay is set to 0, outside the loop you've used 10. Also, outside the loop you've assigned values to count and member, but not outside the loop?

Try this:

function moveants(e, stepdistance)
{
    for (var i = 0; i < 3; i++)
    {
        setTimeout("takeStep(antgroup.childNodes[i], mouseclickX, mouseclickY, 10)", 0);
    }
}

or this:

function moveants(e, stepdistance)
{
    for (var i = 0; i < 3; i++)
    {
        takeStep(antgroup.childNodes[i], mouseclickX, mouseclickY, 10);
    }
}
Nahydrin
  • 13,197
  • 12
  • 59
  • 101
  • When I try putting the entire _takeStep_ function call as a string or if I use the nodeList[] member within the anonymous function the interpreter tells me the first parameter is undefined within the takeStep function. I think it is a variable scope problem. – Michael Phillips May 13 '11 at 12:31
  • The reason I was using setTimeout at all (per your code after "or this") was to try and simulate all of ants moving at the same time (as opposed to serially). That is the behavior I get when I take the setTimeout calls out of the _for_ loop. – Michael Phillips May 13 '11 at 12:32
  • I apologize. I _was_ setting delay of 10 inside the loop as well but I accidentally deleted the _time_ parameter in the post. The _nextAnt.count_ property is used as part of the _condition_ in the _takeStep_ function and actually is set in both (again, sorry!). The _nextAnt.member_ was just for debugging the loop iterations. – Michael Phillips May 13 '11 at 12:33
  • The first one of those won't work; it's generally not a good idea to pass strings to `setTimeout()` – Pointy Jul 23 '12 at 18:49