for循环中的setTimeout不会打印连续的值

我有这个脚本:

for (var i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}

但是3两次都被警告,而不是12

是否有一种方法可以传递i,而不将函数写为字符串?

311405 次浏览

您必须为每个超时函数安排一个不同的“i”副本。

function doSetTimeout(i) {
setTimeout(function() { alert(i); }, 100);
}


for (var i = 1; i <= 2; ++i)
doSetTimeout(i);

如果你不这样做(同样的想法还有其他变体),那么每个计时器处理函数将分享相同的变量“i”。当循环结束时,i的值是多少?这是3 !通过使用中介函数,变量值的复制被创建。由于超时处理程序是在该副本的上下文中创建的,因此它有自己的私有“i”可以使用。

编辑本;随着时间的推移,出现了一些评论,其中明显存在一些混淆,即设置几个超时会导致所有处理程序同时启动。重要的是要理解设置定时器—setTimeout() —几乎不花时间。也就是说,告诉系统“请在1000毫秒后调用这个函数”几乎会立即返回,因为在计时器队列中安装超时请求的过程非常快。

因此,如果超时请求的继承,就像OP中的代码和我的回答中的情况一样,并且每个请求的时间延迟值都是相同的,那么一旦时间流逝,所有的计时器处理程序将一个接一个地快速连续调用。

如果你需要的是每隔一段时间调用处理程序,你可以使用setInterval(),它的调用方式与setTimeout()完全相同,但在重复延迟请求量后会触发不止一次,或者你可以建立超时并将时间值乘以迭代计数器。也就是说,修改我的示例代码:

function doScaledTimeout(i) {
setTimeout(function() {
alert(i);
}, i * 5000);
}

(使用100毫秒超时,效果不会很明显,所以我将数字提高到5000。)i的值乘以基本延迟值,因此在循环中调用该值5次将导致5秒、10秒、15秒、20秒和25秒的延迟。

更新

在2018年,有一个更简单的选择。有了在比函数更窄的作用域中声明变量的新能力,如果这样修改,原始代码将正常工作:

for (let i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}

var不同,let声明本身会导致循环的每次迭代都有一个不同的i

你可以使用一个立即调用的函数表达式(IIFE)来围绕setTimeout创建一个闭包:

for (var i = 1; i <= 3; i++) {
(function(index) {
setTimeout(function() { alert(index); }, i * 1000);
})(i);
}

setTimeout的函数参数在循环变量上关闭。循环在第一个超时之前结束,并显示i的当前值,即3

因为JavaScript变量只有函数作用域,解决方案是将循环变量传递给设置超时的函数。你可以像这样声明和调用这样的函数:

for (var i = 1; i <= 2; i++) {
(function (x) {
setTimeout(function () { alert(x); }, 100);
})(i);
}

回答吗?

我用它来做一个添加物品到购物车的动画——当点击产品“添加”按钮时,一个购物车图标浮动到购物车区域:

function addCartItem(opts) {
for (var i=0; i<opts.qty; i++) {
setTimeout(function() {
console.log('ADDED ONE!');
}, 1000*i);
}
};

说明持续时间单位为单位乘以n周期。

因此,从点击时刻开始,动画开始epoc(每个动画的)是每个一秒单位乘以项目数量的乘积。

epoc的: https://en.wikipedia.org/wiki/Epoch_ (reference_date)

希望这能有所帮助!

可以使用setTimeout的额外参数将参数传递给回调函数。

for (var i = 1; i <= 2; i++) {
setTimeout(function(j) { alert(j) }, 100, i);
}

注意:这在IE9及以下浏览器上不起作用。

好吧,另一个基于科迪的答案但更一般的解决方案是这样的:

function timedAlert(msg, timing){
setTimeout(function(){
alert(msg);
}, timing);
}


function yourFunction(time, counter){
for (var i = 1; i <= counter; i++) {
var msg = i, timing = i * time * 1000; //this is in seconds
timedAlert (msg, timing);
};
}


yourFunction(timeInSeconds, counter); // well here are the values of your choice.

我也遇到过同样的问题这就是我解决的方法。

假设我想要12个间隔为2秒的延迟

    function animate(i){
myVar=setTimeout(function(){
alert(i);
if(i==12){
clearTimeout(myVar);
return;
}
animate(i+1)
},2000)
}


var i=1; //i is the start point 1 to 12 that is
animate(i); //1,2,3,4..12 will be alerted with 2 sec delay

这是因为 !

  1. 超时函数 回调在循环完成后都运行良好。事实上, 作为计时器,即使它是setTimeout(.., 0),所有 这些函数回调在完成后仍然严格运行 这就是为什么有3个被反射了!李< / >
  2. 所有这两个函数,尽管它们已经定义 在每个循环迭代中,分别在相同的共享全局上关闭 作用域,其中实际上只有一个I

解决方案通过使用已执行的自函数(匿名或更好的IIFE)为每次迭代声明一个作用域,并在其中包含的副本,如下所示:

for (var i = 1; i <= 2; i++) {


(function(){


var j = i;
setTimeout(function() { console.log(j) }, 100);


})();


}

更干净的一个

for (var i = 1; i <= 2; i++) {


(function(i){


setTimeout(function() { console.log(i) }, 100);


})(i);


}
在每次迭代中使用IIFE(自执行函数)为每次迭代创建了一个新的作用域 迭代,这给了我们的超时函数回调机会 为每个迭代关闭一个具有变量的新作用域 有正确的每次迭代值供我们访问。< / p >

你可以使用bind方法

for (var i = 1, j = 1; i <= 3; i++, j++) {
setTimeout(function() {
alert(this);
}.bind(i), j * 100);
}
真正的解决方案在这里,但你需要熟悉PHP编程语言。 你必须混合使用PHP和JAVASCRIPT才能达到你的目的

请注意:

<?php
for($i=1;$i<=3;$i++){
echo "<script language='javascript' >
setTimeout(function(){alert('".$i."');},3000);
</script>";
}
?>
它完全是你想要的,但要小心如何使之间的关系

. PHP变量和JAVASCRIPT变量