为什么javascript setTimeout()不能在循环中工作
why does javascript setTimeout() not work in a loop?
考虑以下代码:
<!DOCTYPE html>
<html>
<head>
<script>
function timedText()
{
var x=document.getElementById('txt');
var t = new Array();
t[1] = setTimeout( function(){x.value="2 seconds"}, 2000 );
t[2] = setTimeout( function(){x.value="4 seconds"}, 4000 );
t[3] = setTimeout( function(){x.value="6 seconds"}, 6000 );
}
function timedTextArr()
{
var x=document.getElementById('txt');
var t = new Array();
for( var i = 0 ; i < 3 ; i++ ) {
t[i] = setTimeout( function(){x.value=i*2+" seconds"}, i*2000 );
}
}
</script>
</head>
<body>
<form>
<input type="text" id="txt" />
<input type="button" value="Display timed text!" onclick="timedText()" />
<input type="button" value="Display timed text Arr!" onclick="timedTextArr()" />
</form>
<p>Click on the button above. The input field will tell you when two, four, and six seconds have passed.</p>
</body>
</html>
函数timedText()
起作用,但timedTextArr()
不起作用。这两个函数都将setTimeout()
的返回值分配给数组元素。但在for()
循环中,只有最后一个计时器工作。。。它工作了三次。
这是个虫子吗?
这不是一个bug,看看Javascript中的闭包是什么。
基本上在你的for循环功能
function(){x.value=i*2+" seconds"}
只"看到"i变量的一个实例。
循环结束后,i等于3,所以所有函数都是3。
您需要将调用封装在另一个匿名函数中以创建一个作用域,如下所示:
t[i] = setTimeout( (function(i){ return function(){x.value=i*2+" seconds"}})(i), i*2000 );
外部函数将创建一个新的作用域,在它内部,i将等于循环中i的值,并保持这样。你可以在那里试试:http://jsfiddle.net/6b68E/
函数中的i
指的是循环中的i
,在任何超时触发时为6
。您需要添加一个闭包/作用域:
for (var i = 0 ;i < 3 ;i++ ) {
// create a closure (new scope)
(function() {
// make a local copy of `i` from the outer scope
var _i = i;
// use `i` for values that should refer to the *latest* value.
// use the copy (`_i`) for those that need the point-in-time value.
t[i] = setTimeout(function() { x.value = _i * 2 + " seconds" }, i*2000);
})();
}
更新2023
由于引入了let
,您现在可以将计数器变量的范围扩大到循环中。
for (let i = 0; i < 3; i++) {
setTimeout(() => x.value = `${i * 2} seconds`, i * 2000);
}
获得相同结果的原因是setTimeout
是异步的。这意味着它在脚本的其余部分完成之前不会运行。然后,一旦它运行,i
的值就被设置为等于3,所以所有的函数都运行,它们看到的只是i=3。
主要问题是,由于定时器函数的异步性质,循环在调用第一个超时之前完成,因此在第一个超时运行时,i
设置为2,在其他两个超时中保持不变
为了克服这一点,您应该考虑重构代码以使用间隔,这允许您与闭包同步更改i
的值:
var i=1;
var handle = setInterval( function() {
x.value = (i*2) + "seconds";
i++;
if (i>3) clearInterval(handle);
}, 2000 );
除此之外,循环从0运行到2,而不是从1运行到3,就像在timedText()
中一样
相关文章:
- 另一个ajax调用中的Jquery ajax调用在for循环中没有按预期工作
- AngularJS循环不工作
- For循环在Javascript函数中无法正常工作
- for循环在node.js中的工作方式
- 循环内部的递归函数未按预期工作
- mongoose.js Model.remove在循环中只能工作一次
- JavaScript while循环没有'不能在有条件的情况下工作
- 当if条件被添加到循环中时,Javascript函数将停止工作
- 让循环在我的脚本中工作
- 循环不'不能在“onreadystatechange”中工作
- 如何使此循环工作?它's返回未定义的语言js
- 为..在 Chrome 中循环工作,但不在 IE 中
- JQuery循环工作不正常
- 对象数组中的数据清理:无法进行循环工作
- Javascript倒计时不为php循环工作
- 渐明,淡出DIV上循环工作良好-但不显示第一个DIV
- 我的Javascript for循环工作在除了一个子数组以外的所有子数组上
- 为什么不't this For Canvas中的循环工作
- while循环工作时,页面没有响应
- Javascript循环:for循环工作,但不能映射