在循环内调用setTimeout函数

3
我是JavaScript的新手,正在尝试在for循环内使用setTimeout调用函数。该循环针对nodeList的每个成员执行。
我发现,我使用setTimeout调用的函数实际上只在循环的最后一次迭代期间执行。在下面的示例中,我希望进行三个单独的setTimeout调用,但我发现前两个调用被忽略了。
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);
    }
}

我看到其他帖子描述了如何多次调用setTimeout。令我惊讶的是,如果我将它们从for循环中取出,多次调用将会起作用。
    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);

我就是弄不明白为什么在循环内和循环外调用它们会有所不同。
每种情况下 setInterval 调用都返回有效的返回值,只有在循环的最后一次迭代中,函数才会真正执行。
提前感谢您的任何帮助。

正如Dark Slipstream所说:你在超时延迟中设置了0。尝试使用延迟,例如 setTimeout(function () { takeStep(nextAnt, mouseclickX, mouseclickY, 10) }, 10); - James Khoury
对不起。我在循环内也设置了10秒的延迟,但是我不小心删除了 post 中的时间参数。 - Michael Phillips
2个回答

8

在每次循环中,nextAnt将被覆盖,因此takeStep()将被调用3次,但始终使用相同的参数。

您可以尝试使用以下方式:

(function(a,b,c){
     setTimeout(function(){
                           takeStep(a,b,c,10)}, 0);
      })(nextAnt, mouseclickX, mouseclickY);

哇,你真厉害。这个可行。这是一个闭包吗?我本以为传递给setTimeout每次迭代的参数本质上是私有的,但显然不是这样。我现在的问题是,当我在_for_循环之外进行调用时,它们为什么是私有的?我需要思考一段时间。是我对_for_循环内变量作用域的理解错误还是我对setTimeout工作原理的理解错误?非常感谢。 - Michael Phillips
2
哦哦哦……我想我现在明白了!所以,所有三个调用setTimeout中的_i_实际上仍然“在作用域”内,整个for循环迭代期间都是如此。我发现这篇其他帖子讨论了同样的问题。很奇怪变量范围可以透过一个封闭层次但不能透过两个层次。无论如何,非常酷。再次感谢! - Michael Phillips

0
如果你没有设置延迟,那么为什么要使用setTimeout呢?
在循环中,你的延迟被设置为0,在循环外部你却使用了10。此外,在循环外部你已经赋值给了count和member,但是在循环外部却没有。
试一试这个:
function moveants(e, stepdistance)
{
    for (var i = 0; i < 3; i++)
    {
        setTimeout("takeStep(antgroup.childNodes[i], mouseclickX, mouseclickY, 10)", 0);
    }
}

或者这样:

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

当我尝试将整个_takeStep_函数调用作为字符串放置,或者在匿名函数中使用nodeList[]成员时,解释器告诉我takeStep函数中的第一个参数未定义。我认为这是一个变量作用域问题。 - Michael Phillips
我使用setTimeout的原因(根据您在“或者这个”之后的代码)是为了尝试模拟所有蚂蚁同时移动(而不是串行)。当我将setTimeout调用从_for_循环中删除时,我得到的就是这种行为。 - Michael Phillips
抱歉,我之前确实在循环内部设置了10秒的延迟,但我不小心删除了帖子中的"time"参数。"nextAnt.count"属性是作为"takeStep"函数中"condition"的一部分使用的,而且实际上在两个地方都有设置(再次道歉!)。 "nextAnt.member"仅用于调试循环迭代。 - Michael Phillips
其中第一个不起作用;将字符串传递给 setTimeout() 通常不是一个好主意。 - Pointy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接