【问题标题】:why does javascript setTimeout() not work in a loop? [duplicate]为什么 javascript setTimeout() 不能循环工作? [复制]
【发布时间】:2013-12-04 19:58:19
【问题描述】:

考虑以下代码:

<!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() 循环中,只有最后一个计时器起作用……而且它工作了三遍。

这是一个错误吗?

【问题讨论】:

    标签: javascript settimeout


    【解决方案1】:

    这不是错误,看看 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 将等于循环中 i 的值并保持这样。你可以去那里试试:http://jsfiddle.net/6b68E/

    【讨论】:

    • 有趣。我将研究“javascript 闭包”。谢谢。
    【解决方案2】:

    函数中的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 );
        })();
      }
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        主要问题是由于定时器函数的异步特性,循环在第一次超时调用之前完成运行,因此ifirst 时设置为 2超时运行,其他两个超时保持不变

        要克服这个问题,您应该考虑重构代码以使用 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() 中一样

        【讨论】:

        • 没关系,因为我希望第一个 setTimeout() 为 0 秒,以便用户立即看到。
        猜你喜欢
        • 1970-01-01
        • 2020-02-26
        • 1970-01-01
        • 1970-01-01
        • 2015-10-23
        • 2018-08-08
        • 2019-12-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多