2013-03-10 56 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