2013-03-10 54 views
3

我有一个for循环,为了动画目的,我需要延迟每个循环。如果我删除了setTimeOut函数,那么下面的代码会正确循环,并且变量会正确地通过循环递增,然后执行底线。但是使用setTimeOout函数,底线首先执行,然后for循环执行7次(应该是6),每次告诉我x = 6。显然,我做错了什么。有任何想法吗?JavaScript - for循环中的setTimeout函数导致代码在for循环完成之前执行

for (x = 0; x <= 5; x++) { 
    setTimeout(function() { 
     alert("For loop iteration #" + x); 
    }, 500 * x); 
} 
alert("Code to be executed after completed for loop"); 
+0

你的意思是(应该是6)。顺便说一句,这个问题每隔一天就会被问到,让我搜索... – kapa 2013-03-10 21:10:04

+0

哦,对。我会编辑。 – americanknight 2013-03-10 21:11:05

+0

例如:http://stackoverflow.com/questions/8567118/javascript-settimeout-issue-w-for-loop?rq=1 http://stackoverflow.com/questions/13774004/all-the-settimeouts-inside -javascript-for-loop-happen-at-once – kapa 2013-03-10 21:14:06

回答

1

x是一个全局变量。第一次警报发生时,您已将其增加到6。如果你不希望这种情况发生,使用这样的事情来代替,该递增被称为每500ms里面的功能:

var x = 0; 
var interval = setInterval(function() { 
    alert("Loop iteration #" + x++); 
    if(x==5) { 
     clearInterval(interval); 
     alert("Code to be executed after completing loop"); 
    } 
}, 500); 
+0

为什么值得降级这个?...我意识到我将'for'和'setTimeout'改成了'setInterval',但这是一个建议,它的工作原理也一样。 – Sygmoral 2013-03-10 21:31:52

+0

我没有downvote,但可能解释为什么它downvoted。 1)变量是否全局并不重要。 2)尽管结果仍然相同,但代码与OP代码完全不同。 3)你创建了一个简单的闭包可以解决问题的附加全局变量(=邪恶)。 – Christoph 2013-03-10 21:46:12

+0

这似乎工作得很好,只有最后一行仍然首先执行,我需要它最后执行。 – americanknight 2013-03-10 23:55:27

5

你需要一个封闭保存在封闭的情况对当前x值。

for (var x = 0; x <= 5; x++) { 
    (function(x) { 
     setTimeout(function(){ 
      alert("For loop iteration #" + x); 
      if (x == 5) { 
       setTimeout(function(){ 
        alert("Code to be executed after completed for loop"); 
       }); 
      } 
     }, 5 * x); 

    })(x); 
} 
+0

谢谢。这可以解决循环内部警报的问题,尽管循环后面的行仍然是首先执行,而我需要它在循环结束之前不执行。 – americanknight 2013-03-10 23:52:43

+0

从我的回答中尝试新版本。 – 2013-03-11 06:24:52

+0

为什么setTimeout没有任何延迟参数而不是普通的调用? – Christoph 2013-03-11 14:35:06

3

这是一个共同的概念错误。

  1. JavaScript是无阻塞
  2. 给变量的引用传递,而不是实际值

你必须记住,该变量x是动态的。对x的引用传递给alert("For loop iteration #" + x);而不是值。因此,当alert最终执行时,x将具有执行时的值,而不是启动setTimeout的值!

本质上它是这样的:
您的循环处理完毕,创建6个超时,然后立即显示您的alert("Code to be executed after completed for loop");。然后在一段时间之后,你的超时被执行,然后在循环结束之后全部显示变量x在它的状态 - 6

您需要关闭,以便将值x交给警报,而不是对变量x本身的引用。

for (var x = 0; x <= 5; x++) { 
    (function(z) { 
     setTimeout(function() { 
      alert("For loop iteration #" + z); 
     }, 5 * z); 
    })(x); 
} 

编辑:

为了解决你的第二个问题,你需要使用一个回调函数。 CB函数是您的代码的逻辑延续,但不会立即执行,但需要停止,直到某个点(您的最后一次警报发生)为止。你可以这样实现:

for (var x = 0; x <= 5; x++) { 
    (function(z) { 
     setTimeout(function() { 
      alert("For loop iteration #" + z); 
      if (z===5){ continue_code() } 
     }, 5 * z); 
    })(x); 
} 

function continue_code(){ 
    alert("Code to be executed after completed for loop"); 
    // Here comes all your code 
    // which has to wait for the timeouts from your for loop 
} 

在上一次setTimeout中,您调用继续执行代码的函数。

+0

在JavaScript变量不通过引用传递!它通过分享功能传递! – 2013-03-10 21:29:43

+0

@Silver_Clash我没有谈论参数是如何在函数间切换的,而是函数范围内的表达式。它是一个传递给'alert'而不是实际值的参考。这通常称为通过引用而不是通过价值的呼叫。 – Christoph 2013-03-10 21:41:23

+0

Alert仅使用上下文中的变量对象,它在其中调用。如果我们不使用闭包,那么alert会使用与for循环相同的上下文(alert只是使用变量,而变量不会在setTimeout函数上下文中声明,并且它从作用域链中获取变量,所以没有传递变量! !)!在这种情况下谈论传递变量没有意义,因为我们不改变上下文。 – 2013-03-10 21:46:40