[英]setTimeout with Loop in JavaScript
我有一个非常微不足道的问题。 对于带有setTimeout的简单循环,如下所示:
for (var count = 0; count < 3; count++) {
setTimeout(function() {
alert("Count = " + count);
}, 1000 * count);
}
console提供如下输出:
Count = 3
Count = 3
Count = 3
不知道为什么输出像这样。 有人可以解释一下吗?
这与JavaScript中如何处理范围和提升有关。
您的代码中发生的是JS引擎将您的代码修改为:
var count;
for (count = 0; count < 3; count++) {
setTimeout(function() {
alert("Count = " + count);
}, 1000 * count);
}
当运行setTimeout()
时,它将首先在count
后查看它自己的范围但是它不会找到它然后它将开始在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));
想一想:
在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闭包和匿名函数中很好地解释)
for (var count = 0; count < 3; count++) {
(function(num){setTimeout(function() {
alert("Count = " + num);
}, 1000 * num)})(count);
}
这两个代码示例实际上类似。
第一个示例在每次迭代时调用一个命名函数(do_alert)。
第二个示例在每次迭代时调用CLOSURE匿名函数(就像do_alert
一样)。
这都是SCOPE的问题。
希望有所帮助。
想一想:
count
的价值是什么? 这个循环很久以前完成了...... 这与闭包范围有关。 每个setTimeout回调函数的作用域中都有相同的变量count
。 您正在递增其值并创建一个函数,但该函数的每个实例在其作用域中具有相同的变量count
,并且在回调函数执行时它将具有值3。
您需要在for
循环中的新作用域内创建变量的副本(例如, var localCount = count
)以使其工作。 至于for
不创建一个作用域(这是整个事情的原因),你需要引入一个带有功能范围。
例如
for (var i = 0; i < 5; i++) {
(function() {
var j = i;
setTimeout(function() {
console.log(j)
},
j*100);
})();
}
这是因为所有超时都在循环结束时运行。
超时函数然后获取count的当前值。
这总是3因为for循环已经完成。
这是因为当for循环完成其执行时,count为3,然后调用set timeout。
试试这个:
var count = 0;
setTimeout(function() {
for (count = 0; count < 3; count++) {
alert("Count = " + count);
}
}, 1000* count);
更好的解决方案是“在这种情况下忘记循环和递归”并使用“setInterval”的这个组合包括“setTimeOut”:
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:定时循环的示例,但对于反应循环,您可以使用事件,承诺异步等待..
这里很容易解决的是利用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);
首先,setTimeout(函数,毫秒)是一个函数,它在“毫秒”毫秒之后执行函数。
请记住,JS将函数视为对象,因此for(...)循环最初会产生如下内容:
setTimeout( ... ) setTimeout( ... ) setTimeout( ... )
现在setTimeout()函数将逐个执行。
setTimeout()函数将尝试在当前范围内查找count变量。 如果失败了,它将转到外部范围并将找到count,其值已经由for循环递增到3。
现在,开始执行....第一个警报立即显示,毫秒为0,第二个警报显示1000毫秒后,然后第三个警报显示2000毫秒后。 所有这些都显示Count = 3
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.