setTimeout with Loop in JavaScript
我有一个很小的问题。对于设置超时的简单循环,如下所示:
1 2 3 4 5 | for (var count = 0; count < 3; count++) { setTimeout(function() { alert("Count =" + count); }, 1000 * count); } |
控制台提供如下输出:
1 2 3 | Count = 3 Count = 3 Count = 3 |
不知道为什么输出是这样的。有人能解释吗?
这与JavaScript中如何处理范围界定和提升有关。
您的代码中发生的事情是JS引擎将您的代码修改为:
1 2 3 4 5 6 7 | var count; for (count = 0; count < 3; count++) { setTimeout(function() { alert("Count =" + count); }, 1000 * count); } |
当
更多代码详细解释了您的代码实际上如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | //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)); |
这样想:
1000*N毫秒结束后,计数值是多少?
当然是3,因为foor循环的结束时间早于1000*n ms的超时时间。
要打印1、2、3,您需要以下内容:
1 2 3 4 5 6 7 8 9 | for (var count = 0; count < 3; count++) { do_alert(num); } function do_alert(num) { setTimeout(function() { alert("Count =" + num); }, 1000 * num); } |
另一种方法是使其成为
1 2 3 4 5 | for (var count = 0; count < 3; count++) { (function(num){setTimeout(function() { alert("Count =" + num); }, 1000 * num)})(count); } |
这两个代码示例实际上工作原理类似。
第一个示例在每次迭代中调用一个命名函数(do_alert)。
第二个示例在每次迭代中调用一个闭包匿名函数(就像
这都是范围问题。
希望有帮助。
这与闭包范围有关。每个setTimeout回调函数的作用域中都有相同的变量
您需要在
例如
1 2 3 4 5 6 7 8 9 | for (var i = 0; i < 5; i++) { (function() { var j = i; setTimeout(function() { console.log(j) }, j*100); })(); } |
想想看:
首先,setTimeout(函数,毫秒)是一个函数,它需要在"毫秒"毫秒之后执行一个函数。
记住,JS将函数视为对象,因此for(…)循环最初将生成如下内容:
setTimeout( ... )
setTimeout( ... )
现在,setTimeout()函数将逐个执行。
函数的作用域将尝试查找当前作用域中的count变量。如果失败,它将转到外部作用域并找到count,其值已经由for循环增加到3。
现在,开始执行……第一个警报立即显示,毫秒为0,第二个警报在1000毫秒后显示,第三个警报在2000毫秒后显示。所有警报都显示
这里简单的解决方法是使用ES6
1 2 3 4 5 6 | for (let count = 0; count < 3; count++) { setTimeout(function() { alert("Count =" + count); }, 1000 * count); } |
或者您可以创建一个递归函数来完成该任务,如下所示:
1 2 3 4 5 6 7 8 9 10 | function timedAlert(n) { if (n < 3) { setTimeout(function() { alert("Count =" + n); timedAlert(++n); }, 1000); } } timedAlert(0); |
更好的解决方案是在这种情况下"忘记循环和递归",并使用"setInterval"的组合包括"setTimeout"的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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(''+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将在同一时间开始倒数",因此请设置不同的超时来安排执行。
ps 2:计时循环的例子,但是对于一个反应循环,您可以使用事件,promise async wait.。
这是因为在for循环完成执行时,计数为3,然后调用set timeout。
试试这个:
1 2 3 4 5 6 | var count = 0; setTimeout(function() { for (count = 0; count < 3; count++) { alert("Count =" + count); } }, 1000* count); |
这是因为所有超时都在循环结束时运行。
然后,超时函数获取当前的count值。
这总是3,因为for循环已经完成。