在JavaScript中使用Loop的setTimeout

时间:2013-10-07 09:52:34

标签: javascript

我有一个非常微不足道的问题。对于带有setTimeout的简单循环,如下所示:

for (var count = 0; count < 3; count++) {
    setTimeout(function() {
        alert("Count = " + count);
    }, 1000 * count);
}

console提供如下输出:

Count = 3
Count = 3
Count = 3

不确定为什么输出会像这样。有人可以解释一下吗?

9 个答案:

答案 0 :(得分:6)

这与JavaScript如何处理范围和提升有关。

代码中发生的是JS引擎将代码修改为:

var count;

for (count = 0; count < 3; count++) {
    setTimeout(function() {
        alert("Count = " + count);
    }, 1000 * count);
}

setTimeout()正在运行时,它将首先在count之后查看它自己的范围,但它不会找到它然后它将开始查看关闭的函数(这称为closures)在setTimeout函数之前,直到找到var count语句,该语句的值为3,因为循环将在第一个超时函数执行之前完成。

更多代码 - 解释说您的代码实际上是这样的:

//first iteration
var count = 0; //this is 1 because of count++ in your for loop.

for (count = 0; count < 3; count++) { 
    setTimeout(function() {
        alert("Count = " + 1);
    }, 1000 * 1);
}
count = count + 1; //count = 1

//second iteration
var count = 1;

for (count = 0; count < 3; count++) {
    setTimeout(function() {
        alert("Count = " + 2);
    }, 1000 * 2);
}
count = count + 1; //count = 2

//third iteration
var count = 2;

for (count = 0; count < 3; count++) {
    setTimeout(function() {
        alert("Count = " + 3);
    }, 1000 * 3);
}
count = count + 1; //count = 3

//after 1000 ms
window.setTimeout(alert(count));
//after 2000 ms
window.setTimeout(alert(count));
//after 3000 ms
window.setTimeout(alert(count));

答案 1 :(得分:4)

这样想:

1000 * n毫秒结束后,计数的值是多少?

当然它将是3,因为foor循环比1000 * n ms的超时早结束。

要打印1,2,3,您需要以下内容:

for (var count = 0; count < 3; count++) {
    do_alert(num);
}

function do_alert(num) {
    setTimeout(function() {
        alert("Count = " + num);
    }, 1000 * num);
}

另一种方法是将其设为closure function(在JavaScript closures vs. anonymous functions中解释得很好)

for (var count = 0; count < 3; count++) {
    (function(num){setTimeout(function() {
        alert("Count = " + num);
    }, 1000 * num)})(count);
}

这两个代码示例实际上的工作方式类似。

第一个示例在每次迭代时调用一个命名函数(do_alert)。

第二个示例在每次迭代时调用CLOSURE匿名函数(就像do_alert一样)。

这都是SCOPE的问题。

希望有所帮助。

答案 2 :(得分:1)

想一想:

  1. 代码执行循环,在该循环中设置一些代码以便稍后运行。
  2. 循环结束。
  3. 执行setTimeout代码。 count的价值是什么?这个循环很久以前就完成了......

答案 3 :(得分:1)

这与闭包范围有关。每个setTimeout回调函数的作用域中都有相同的变量count。您正在递增其值并创建一个函数,但该函数的每个实例在其作用域中都具有相同的变量count,并且在回调函数执行时它将具有值3.

您需要在var localCount = count循环中的新范围内创建变量的副本(例如for)才能使其正常工作。由于for没有创建范围(这是整个事物的原因),因此需要引入一个具有函数范围的范围。

e.g。

for (var i = 0; i < 5; i++) {
  (function() {
    var j = i;
    setTimeout(function() {
      console.log(j)
    },
    j*100);
   })();
 }

答案 4 :(得分:0)

这是因为所有超时都在循环结束时运行。

超时函数然后取当前的count值。

并且那总是3,因为for循环已经完成。

答案 5 :(得分:0)

这是因为当for循环完成执行时,count为3,然后调用set timeout。

试试这个:

var count = 0; 
setTimeout(function() {
       for (count = 0; count < 3; count++) {
           alert("Count = " + count);
        }
}, 1000* count);

答案 6 :(得分:0)

更好的解决方案IS&#34;在这种情况下忘记循环和递归&#34; 并使用&#34; setInterval&#34;的组合包括&#34; setTimeOut&#34; s:

    function iAsk(lvl){
        var i=0;
        var intr =setInterval(function(){ // start the loop 
            i++; // increment it
            if(i>lvl){ // check if the end round reached.
                clearInterval(intr);
                return;
            }
            setTimeout(function(){
                $(".imag").prop("src",pPng); // do first bla bla bla after 50 millisecond
            },50);
            setTimeout(function(){
                 // do another bla bla bla after 100 millisecond.
                seq[i-1]=(Math.ceil(Math.random()*4)).toString();
                $("#hh").after('<br>'+i + ' : rand= '+(Math.ceil(Math.random()*4)).toString()+' > '+seq[i-1]);
                $("#d"+seq[i-1]).prop("src",pGif);
                var d =document.getElementById('aud');
                d.play();                   
            },100);
            setTimeout(function(){
                // keep adding bla bla bla till you done :)
                $("#d"+seq[i-1]).prop("src",pPng);
            },900);
        },1000); // loop waiting time must be >= 900 (biggest timeOut for inside actions)
    }
PS:理解(setTimeOut)的真实行为:它们都将在同一时间开始&#34;三个bla bla bla将在同一时刻开始倒计时#34;所以要做一个不同的超时来安排执行。

PS 2:定时循环的示例,但对于反应循环,您可以使用事件,承诺异步等待..

答案 7 :(得分:0)

这里的简单修复是利用es6 let局部变量。你的代码看起来几乎一样,只不过它会做你期望的事情:)

 for (let count = 0; count < 3; count++) {
    setTimeout(function() {
        alert("Count = " + count);
    }, 1000 * count);

}

或者你可以创建一个递归函数来完成这项工作,如下所示:

function timedAlert(n) {
  if (n < 3) {
    setTimeout(function() {
        alert("Count = " + n);
        timedAlert(++n);
    }, 1000);
  }
}

timedAlert(0);

答案 8 :(得分:0)

首先,setTimeout(函数,毫秒)是一个函数,它在&#34;毫秒&#34;之后执行函数。毫秒。

请记住,JS将函数视为对象,因此for(...)循环最初会产生类似于:

的函数

Count = 3

现在setTimeout()函数将逐个执行。

setTimeout()函数将尝试在当前范围内查找count变量。如果失败了,它将转到外部范围并找到count,其值已经被for循环增加到3。

现在,开始执行....第一个警报立即显示,毫秒为0,第二个警报显示1000毫秒后,然后第三个警报显示2000毫秒后。所有这些都显示calculateMarginForYScaleTicks() { let maxWidth = 0; let textFormatter = d3.format(","); let tempYScale = d3 .scaleLinear() .range([0, 0]) .domain([0, d3.max(data, d => d.value)]); d3 .select("#svg") .selectAll("text.foo") .data(tempYScale.ticks()) .enter() .append("text") .text(d => textFormatter(d)) .each(function() { maxWidth = Math.max(this.getBBox().width, maxWidth); }) .remove(); return maxWidth; }