for循环中的setTimeout不打印连续值
我有这个脚本:
for (var i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}
但 3
两次都会收到警报,而不是 1
然后是 2
.
But 3
is alerted both times, instead of 1
then 2
.
有没有办法传递i
,而不把函数写成字符串?
Is there a way to pass i
, without writing the function as a string?
推荐答案
您必须为每个超时函数安排一个不同的i"副本.
You have to arrange for a distinct copy of "i" to be present for each of the timeout functions.
function doSetTimeout(i) {
setTimeout(function() { alert(i); }, 100);
}
for (var i = 1; i <= 2; ++i)
doSetTimeout(i);
如果您不这样做(并且同样的想法还有其他变体),那么每个计时器处理函数将共享相同的变量i".循环结束后,i"的值是多少?是3!通过使用中间函数,可以生成变量值的副本.由于超时处理程序是在该副本的上下文中创建的,因此它有自己的私有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 share the same variable "i". When the loop is finished, what's the value of "i"? It's 3! By using an intermediating function, a copy of 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.
编辑 —随着时间的推移,出现了一些评论,其中一些混淆显而易见,因为设置一些超时会导致处理程序同时触发.了解设置计时器的过程很重要—对 setTimeout()
的调用 —几乎不花时间.也就是说,告诉系统请在 1000 毫秒后调用此函数"几乎会立即返回,因为在定时器队列中安装超时请求的过程非常快.
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 up the 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.
因此,如果发出 连续 个超时请求,就像 OP 中的代码和我的回答中的情况一样,并且每个请求的时间延迟值相同,那么一次这段时间过去了,所有的计时器处理程序将被快速连续地一个接一个地调用.
Thus, if a succession of 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.
如果您需要每隔一段时间调用处理程序,您可以使用 setInterval()
,它的调用方式与 setTimeout()
完全相同,但会在请求数量的重复延迟后多次触发,或者您可以建立超时并将时间值乘以迭代计数器.也就是修改我的示例代码:
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:
function doScaledTimeout(i) {
setTimeout(function() {
alert(i);
}, i * 5000);
}
(用100
毫秒超时,效果不会很明显,所以我把这个数字提高到5000.)i
的值乘以基本延迟值,因此在循环中调用 5 次将导致延迟 5 秒、10 秒、15 秒、20 秒和 25 秒.
(With a 100
millisecond timeout, the effect won't be very obvious, so I bumped the number up to 5000.) The value of i
is 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.
更新
在 2018 年,有一个更简单的选择.有了在比函数更窄的范围内声明变量的新功能,如果修改了原始代码,则可以正常工作:
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:
for (let i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}
let
声明与 var
不同,它本身将导致循环的每次迭代都有一个不同的 i
.
The let
declaration, unlike var
, will itself cause there to be a distinct i
for each iteration of the loop.
相关文章