为什么javascript setTimeout()不能在循环中工作?

时间:2013-12-04 19:58:20

标签: javascript settimeout

请考虑以下代码:

<!DOCTYPE html>
<html>
<head>
<script>
function timedText()
{
  var x=document.getElementById('txt');
  var t = new Array();
  t[1] = setTimeout( function(){x.value="2 seconds"}, 2000 );
  t[2] = setTimeout( function(){x.value="4 seconds"}, 4000 );
  t[3] = setTimeout( function(){x.value="6 seconds"}, 6000 );
}
function timedTextArr()
{
  var x=document.getElementById('txt');
  var t = new Array();
  for( var i = 0 ; i < 3 ; i++ ) {
    t[i] = setTimeout( function(){x.value=i*2+" seconds"}, i*2000 );
  }
}
</script>
</head>
<body>
<form>
<input type="text" id="txt" />
<input type="button" value="Display timed text!" onclick="timedText()" />
<input type="button" value="Display timed text Arr!" onclick="timedTextArr()" />
</form>
<p>Click on the button above. The input field will tell you when two, four, and six seconds have passed.</p>
</body>
</html>

函数timedText()有效,但timedTextArr()没有。两个函数都将返回值从setTimeout()分配给数组元素。但是在for()循环中,只有最后一个计时器工作......并且它可以工作三次。

这是一个错误吗?

4 个答案:

答案 0 :(得分:5)

这不是一个bug,看看Javascript中的闭包。

基本上你的for循环函数

function(){x.value=i*2+" seconds"}

只“看到”i变量的一个实例。

因此,一旦循环结束,i等于3,所有函数都为3。

您需要将调用包装在另一个匿名函数中以创建范围,如下所示:

t[i] = setTimeout( (function(i){ return function(){x.value=i*2+" seconds"}})(i), i*2000 );

外部函数将创建一个新范围,在其中我将等于循环中i的值并保持这样。你可以在那里试试:http://jsfiddle.net/6b68E/

答案 1 :(得分:5)

您的函数中的i引用循环中的i,在任何超时发生时为6。您需要添加一个闭包/范围:

  for( var i = 0 ; i < 3 ; i++ ) {
    (function(){    // create a closure (new scope)
      var _i = i;   // make a local copy of `i` from the outer scope
      t[i] = setTimeout( function(){x.value=_i*2+" seconds"}, i*2000 );
    })();
  }

答案 2 :(得分:1)

主要问题是由于计时器功能的异步性质,循环在第一次超时被调用之前就已经完成,因此在第一次时将i设置为2 em>超时运行,并且对于其他两个超时保持不变

要解决此问题,您应该考虑重构代码以使用 interval ,这样您就可以与闭包同步更改i的值:

var i=1;

var handle = setInterval( function() {

x.value = (i*2) + "seconds";
i++;
if (i>3) clearInterval(handle);

}, 2000 );

除此之外,循环从0运行到2,而不是1到3,如timedText()

答案 3 :(得分:1)

您获得相同结果的原因是setTimeout是异步的。这意味着它在脚本的其余部分完成后才会运行。然后一旦运行,i的值设置为等于3,所以所有函数都运行,他们看到的只是i = 3.