如何在 JavaScript 循环中添加延迟?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3583724/
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
How do I add a delay in a JavaScript loop?
提问by olidev
I would like to add a delay/sleep inside a whileloop:
我想在while循环中添加延迟/睡眠:
I tried it like this:
我是这样试的:
alert('hi');
for(var start = 1; start < 10; start++) {
setTimeout(function () {
alert('hello');
}, 3000);
}
Only the first scenario is true: after showing alert('hi'), it will be waiting for 3 seconds then alert('hello')will be displayed but then alert('hello')will be repeatedly constantly.
只有第一种情况是正确的:显示后alert('hi'),将等待 3 秒然后alert('hello')显示,然后alert('hello')会不断重复。
What I would like is that after alert('hello')is shown 3 seconds after alert('hi')then it needs to wait for 3 seconds for the second time alert('hello')and so on.
我想要的是 afteralert('hello')显示 3 秒后alert('hi'),第二次需要等待 3 秒alert('hello'),依此类推。
回答by Daniel Vassallo
The setTimeout()function is non-blocking and will return immediately. Therefore your loop will iterate very quickly and it will initiate 3-second timeout triggers one after the other in quick succession. That is why your first alerts pops up after 3 seconds, and all the rest follow in succession without any delay.
该setTimeout()函数是非阻塞的,将立即返回。因此,您的循环将非常快速地迭代,并且会快速连续地启动 3 秒超时触发。这就是为什么您的第一个警报会在 3 秒后弹出,而其余所有警报会毫无延迟地连续出现。
You may want to use something like this instead:
你可能想使用这样的东西:
var i = 1; // set your counter to 1
function myLoop() { // create a loop function
setTimeout(function() { // call a 3s setTimeout when the loop is called
console.log('hello'); // your code here
i++; // increment the counter
if (i < 10) { // if the counter < 10, call the loop function
myLoop(); // .. again which will trigger another
} // .. setTimeout()
}, 3000)
}
myLoop(); // start the loop
You could also neaten it up, by using a self invoking function, passing the number of iterations as an argument:
您还可以通过使用自调用函数将迭代次数作为参数传递来整理它:
(function myLoop(i) {
setTimeout(function() {
console.log('hello'); // your code here
if (--i) myLoop(i); // decrement i and call myLoop again if i > 0
}, 3000)
})(10); // pass the number of iterations as an argument
回答by cji
Try something like this:
尝试这样的事情:
var i = 0, howManyTimes = 10;
function f() {
alert( "hi" );
i++;
if( i < howManyTimes ){
setTimeout( f, 3000 );
}
}
f();
回答by Saket Mehta
If using ES6, you could use letto achieve this:
如果使用 ES6,您可以使用以下方法let来实现:
for (let i=1; i<10; i++) {
setTimeout( function timer(){
alert("hello world");
}, i*3000 );
}
What letdoes is declare ifor each iteration, not the loop. This way, what is passed to setTimeoutis exactly what we want.
什么let是i为每次迭代声明,而不是循环。这样,传递给setTimeout的正是我们想要的。
回答by Jonas Wilms
Since ES7 theres a better way to awaita loop:
由于 ES7 有一个更好的方法来等待循环:
// Returns a Promise that resolves after "ms" Milliseconds
function timer(ms) {
return new Promise(res => setTimeout(res, ms));
}
async function load () { // We need to wrap the loop into an async function for this to work
for (var i = 0; i < 3; i++) {
console.log(i);
await timer(3000); // then the created Promise can be awaited
}
}
load();
When the engine reaches the awaitpart, it sets a timeout and halts the execution of the async function. Then when the timeout completes, execution continues at that point. That's quite useful as you can delay (1) nested loops, (2) conditionally, (3) nested functions:
当引擎到达await零件时,它会设置超时并停止执行async function. 然后当超时完成时,在该点继续执行。这非常有用,因为您可以延迟 (1) 嵌套循环,(2) 有条件地,(3) 嵌套函数:
async function task(i) { // 3
await timer(1000);
console.log(`Task ${i} done!`);
}
async function main() {
for(let i = 0; i < 100; i+= 10) {
for(let j = 0; j < 10; j++) { // 1
if(j % 2) { // 2
await task(i + j);
}
}
}
}
main();
function timer(ms) { return new Promise(res => setTimeout(res, ms)); }
While ES7 is now supported by NodeJS and modern browsers, you might want to transpile it with BabelJSso that it runs everywhere.
虽然NodeJS和现代浏览器现在支持 ES7,但您可能希望使用BabelJS 对其进行转译,以便它可以在任何地方运行。
回答by Felix Kling
Another way is to multiply the time to timeout, but note that this is not like sleep. Code after the loop will be executed immediately, only the execution of the callback function is deferred.
另一种方法是乘以超时时间,但请注意,这与 sleep 不同。循环之后的代码会立即执行,只有回调函数的执行才会被推迟。
for (var start = 1; start < 10; start++)
setTimeout(function () { alert('hello'); }, 3000 * start);
The first timeout will be set to 3000 * 1, the second to 3000 * 2and so on.
第一个超时将设置为3000 * 1,第二个为3000 * 2,依此类推。
回答by BGerrissen
I think you need something like this:
我认为你需要这样的东西:
var TimedQueue = function(defaultDelay){
this.queue = [];
this.index = 0;
this.defaultDelay = defaultDelay || 3000;
};
TimedQueue.prototype = {
add: function(fn, delay){
this.queue.push({
fn: fn,
delay: delay
});
},
run: function(index){
(index || index === 0) && (this.index = index);
this.next();
},
next: function(){
var self = this
, i = this.index++
, at = this.queue[i]
, next = this.queue[this.index]
if(!at) return;
at.fn();
next && setTimeout(function(){
self.next();
}, next.delay||this.defaultDelay);
},
reset: function(){
this.index = 0;
}
}
Test code:
测试代码:
var now = +new Date();
var x = new TimedQueue(2000);
x.add(function(){
console.log('hey');
console.log(+new Date() - now);
});
x.add(function(){
console.log('ho');
console.log(+new Date() - now);
}, 3000);
x.add(function(){
console.log('bye');
console.log(+new Date() - now);
});
x.run();
Note: using alerts stalls javascript execution till you close the alert. It might be more code than you asked for, but this is a robust reusable solution.
注意:使用警报会停止 javascript 执行,直到您关闭警报。它可能比您要求的代码多,但这是一个强大的可重用解决方案。
回答by Abel Terefe
I would probably use setInteval. Like this,
我可能会使用setInteval. 像这样,
var period = 1000; // ms
var endTime = 10000; // ms
var counter = 0;
var sleepyAlert = setInterval(function(){
alert('Hello');
if(counter === endTime){
clearInterval(sleepyAlert);
}
counter += period;
}, period);
回答by Gsvp Nagaraju
This will work
这将工作
for (var i = 0; i < 10; i++) {
(function(i) {
setTimeout(function() { console.log(i); }, 100 * i);
})(i);
}
Try this fiddle: https://jsfiddle.net/wgdx8zqq/
试试这个小提琴:https: //jsfiddle.net/wgdx8zqq/
回答by Itay Radotzki
In ES6 (ECMAScript 2015) you can iterate with delay with generatorand interval.
在 ES6 (ECMAScript 2015) 中,您可以使用生成器和间隔进行延迟迭代。
Generators, a new feature of ECMAScript 6, are functions that can be paused and resumed. Calling genFunc does not execute it. Instead, it returns a so-called generator object that lets us control genFunc's execution. genFunc() is initially suspended at the beginning of its body. The method genObj.next() continues the execution of genFunc, until the next yield. (Exploring ES6)
生成器是 ECMAScript 6 的一个新特性,是可以暂停和恢复的函数。调用 genFunc 不会执行它。相反,它返回一个所谓的生成器对象,让我们可以控制 genFunc 的执行。genFunc() 最初暂停在其主体的开头。方法 genObj.next() 继续执行 genFunc,直到下一个 yield。 (探索 ES6)
Code example:
代码示例:
let arr = [1, 2, 3, 'b'];
let genObj = genFunc();
let val = genObj.next();
console.log(val.value);
let interval = setInterval(() => {
val = genObj.next();
if (val.done) {
clearInterval(interval);
} else {
console.log(val.value);
}
}, 1000);
function* genFunc() {
for(let item of arr) {
yield item;
}
}
So if you are using ES6, that the most elegant way to achieve loop with delay (for my opinion).
因此,如果您使用的是 ES6,这是实现延迟循环的最优雅方式(我认为)。
回答by Dave Bryand
I do this with Bluebird's Promise.delayand recursion.
我用 BluebirdPromise.delay和递归来做到这一点。
function myLoop(i) {
return Promise.delay(1000)
.then(function() {
if (i > 0) {
alert('hello');
return myLoop(i -= 1);
}
});
}
myLoop(3);
<script src="//cdnjs.cloudflare.com/ajax/libs/bluebird/2.9.4/bluebird.min.js"></script>

