2013-10-07 73 views
5

我有一個非常微不足道的問題。對於一個簡單的循環與setTimeout的,就像這樣:setTimeout在JavaScript中使用Loop

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

控制檯,像這樣的輸出:

Count = 3 
Count = 3 
Count = 3 

不知道爲什麼輸出這樣。任何人都可以解釋,請嗎?

+2

的可能重複[傳遞函數setTimeout在循環中:總是最後一個值?](http://stackoverflow.com/questions/6425062/passing-functions-to-settimeout-in-a-loop-always-the-last-value) – Quentin

回答

5

這與如何在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

想想看:

  1. 的代碼執行一個循環,在循環它還設定了一些代碼,以便以後運行。
  2. 循環結束。
  3. 執行setTimeout代碼。伯爵的價值是什麼?循環完成很久以前...
1

這是關閉作用域範圍。每個setTimeout回調函數的範圍中都有相同的變量count。您正在遞增其值並創建一個函數,但函數的每個實例在其作用域中具有相同的變量count,並且在回調函數執行時它將具有值3.

您需要創建一個副本的變量(例如var localCount = count)位於for循環的新範圍內,以使其工作。由於for不會創建範圍(這是整個事件的原因),因此您需要引入一個具有函數範圍的範圍。

例如

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

doesn 't work:'for(var i = 0; i <5; i ++)var j = i; ()函數(){ console.log(j) },j * 100); }' – rofrol

+0

這是因爲'j'變量被提升到父範圍,因爲for循環沒有創建一個。 (var i = 0; i <5; i ++){(function(){var j = i; setTimeout(function(){console.log(j))需要創建一個新的函數範圍來隔離'j': )},j * 100);})()}' – Joe

+0

我編輯了我的問題以迴應您的問題。 – Joe

0

這是因爲當循環完成了所有超時被運行。

超時函數然後採用count的當前值。

而且總是3,因爲for循環已經完成。

0

這是因爲在for循環完成其執行時count爲3,然後調用設置的超時時間。

試試這個:

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

想想這樣的:

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的問題。

希望有所幫助。

0

更好的辦法是「忘記了這兩個循環和遞歸」在這種情況下和使用「的setInterval」包括「setTimeout的」 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的真正行爲):它們都將在同一時間開始「三個bla bla bla將在同一時刻開始倒計時」,因此請安排執行的不同超時。

PS 2:定時循環的例子,但對於一個反應循環,你可以使用事件,答應異步等待..

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);