Javascript for-loop 中的 setTimeout 不打印连续值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5226285/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 16:15:10  来源:igfitidea点击:

setTimeout in for-loop does not print consecutive values

javascript

提问by Ilyssis

I have this script:

我有这个脚本:

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

But 3is alerted both times, instead of 1then 2.

但是3两次都收到警报,而不是1then 2

Is there a way to pass i, without writing the function as a string?

有没有办法 pass i,而不将函数写成字符串?

回答by Pointy

You have to arrange for a distinct copy of "i" to be present for each of the timeout functions.

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

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

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

If you don't do something like this (and there are other variations on this same idea), then each of the timer handler functions will sharethe same variable "i". When the loop is finished, what's the value of "i"? It's 3! By using an intermediating function, a copyof the value of the variable is made. Since the timeout handler is created in the context of that copy, it has its own private "i" to use.

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

edit— there have been a couple of comments over time in which some confusion was evident over the fact that setting up a few timeouts causes the handlers to all fire at the same time. It's important to understand that the process of setting upthe timer — the calls to setTimeout()— take almost no time at all. That is, telling the system, "Please call this function after 1000 milliseconds" will return almost immediately, as the process of installing the timeout request in the timer queue is very fast.

编辑- 随着时间的推移,有一些评论很明显,因为设置几个超时会导致处理程序同时触发。重要的是要了解设置计时器的过程——调用setTimeout()——几乎不需要任何时间。也就是说,告诉系统“请在 1000 毫秒后调用此函数”几乎会立即返回,因为在计时器队列中安装超时请求的过程非常快。

Thus, if a successionof timeout requests is made, as is the case in the code in the OP and in my answer, and the time delay value is the same for each one, then once that amount of time has elapsed all the timer handlers will be called one after another in rapid succession.

因此,如果发出了一系列超时请求,就像 OP 中的代码和我的答案中的情况一样,并且每个请求的时间延迟值都相同,那么一旦该时间量过去,所有计时器处理程序将被一个接一个地快速连续调用。

If what you need is for the handlers to be called at intervals, you can either use setInterval(), which is called exactly like setTimeout()but which will fire more than once after repeated delays of the requested amount, or instead you can establish the timeouts and multiply the time value by your iteration counter. That is, to modify my example code:

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

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

(With a 100millisecond timeout, the effect won't be very obvious, so I bumped the number up to 5000.) The value of iis multiplied by the base delay value, so calling that 5 times in a loop will result in delays of 5 seconds, 10 seconds, 15 seconds, 20 seconds, and 25 seconds.

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

Update

更新

Here in 2018, there is a simpler alternative. With the new ability to declare variables in scopes more narrow than functions, the original code would work if so modified:

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

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

The letdeclaration, unlike var, will itself cause there to be a distinct ifor each iteration of the loop.

let声明,不像var,本身就会造成那里是一个独特i的循环的每个迭代。

回答by Darin Dimitrov

You can use an immediately-invoked function expression (IIFE) to create a closure around setTimeout:

您可以使用立即调用的函数表达式 ( IIFE) 在 周围创建闭包setTimeout

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

回答by Mehdi Raash

This's Because!

这是因为

  1. The timeoutfunction callbacks are all running well after the completion of the loop. In fact, as timers go, even if it was setTimeout(.., 0) on each iteration, all those function callbacks would still run strictly after the completion of the loop, that's why 3 was reflected!
  2. all two of those functions, though they are defined separately in each loop iteration, are closed over the same shared global scope, which has, in fact, only one i in it.
  1. 超时循环完成后函数回调都运行良好。事实上,随着定时器的运行,即使每次迭代都是 setTimeout(.., 0) ,所有这些函数回调仍然会在循环完成后严格运行,这就是为什么 3 被反映!
  2. 尽管它们在每次循环迭代中分别定义,但所有这两个函数都在相同的共享全局范围内关闭,实际上,其中只有一个 i 。

the Solution's declaring a single scope for each iteration by using a self-function executed(anonymous one or better IIFE) and having a copy of iin it, like this:

解决方案的通过使用执行的自功能(匿名一个或更好声明对于每次迭代单个范围IIFE)与具有的副本在里面,这样的:

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

     (function(){

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

     })();

}

the cleaner one would be

更干净的人会是

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

     (function(i){ 

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

     })(i);

}

The use of an IIFE(self-executed function) inside each iteration created a new scope for each iteration, which gave our timeout function callbacks the opportunity to close over a new scope for each iteration, one which had a variable with the right per-iteration value in it for us to access.

在每次迭代中使用IIFE(自执行函数)为每次迭代创建了一个新的作用域,这让我们的超时函数回调有机会为每次迭代关闭一个新的作用域,其中一个变量具有正确的 per-我们可以访问其中的迭代值。

回答by harto

The function argument to setTimeoutis closing over the loop variable. The loop finishes before the first timeout and displays the current value of i, which is 3.

的函数参数setTimeout正在关闭循环变量。循环在第一次超时之前完成并显示 的当前值i,即3

Because JavaScript variables only have function scope, the solution is to pass the loop variable to a function that sets the timeout. You can declare and call such a function like this:

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

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

回答by Mevin Babu

You can use the extra arguments to setTimeoutto pass parameters to the callback function.

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

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

Note: This doesn't work on IE9 and below browsers.

注意:这不适用于 IE9 及以下浏览器。

回答by Cody

ANSWER?

答案

I'm using it for an animation for adding items to a cart - a cart icon floats to the cart area from the product "add" button, when clicked:

我将它用于将商品添加到购物车的动画 - 单击时,购物车图标从产品“添加”按钮浮动到购物车区域:

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

NOTE the duration is in unit times nepocs.

注意持续时间以单位时间 nepocs为单位

So starting at the the click moment, the animations start epoc (of EACH animation) is the product of each one-second-unit multiplied by the number of items.

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

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

时代https: //en.wikipedia.org/wiki/Epoch_(reference_date )

Hope this helps!

希望这可以帮助!

回答by Raghavendra

You could use bindmethod

你可以使用bind方法

for (var i = 1, j = 1; i <= 3; i++, j++) {
    setTimeout(function() {
        alert(this);
    }.bind(i), j * 100);
}

回答by maisafie

Well, another working solution based on Cody's answer but a little more general can be something like this:

好吧,另一个基于 Cody 答案的工作解决方案,但更一般的可以是这样的:

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.

回答by Raj Sharma

I had the same problem once this is how I solved it.

一旦这是我解决它的方式,我就遇到了同样的问题。

Suppose I want 12 delays with an interval of 2 secs

假设我想要 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

回答by reza

the real solution is here, but you need to be familiar with PHP programing language. you must mix PHP and JAVASCRIPT orders in order to reach to your purpose.

真正的解决方案在这里,但您需要熟悉 PHP 编程语言。您必须混合使用 PHP 和 JAVASCRIPT 命令才能达到您的目的。

pay attention to this :

注意这一点:

<?php 
for($i=1;$i<=3;$i++){
echo "<script language='javascript' >
setTimeout(function(){alert('".$i."');},3000);  
</script>";
}
?> 

It exactly does what you want, but be careful about how to make ralation between PHP variables and JAVASCRIPT ones.

它完全符合您的要求,但要注意如何在 PHP 变量和 JAVASCRIPT 变量之间建立关系。