如何在 JavaScript 循环中添加延迟?

IT技术 javascript loops sleep
2021-01-06 15:56:44

我想在while循环中添加延迟/睡眠

我是这样试的:

alert('hi');

for(var start = 1; start < 10; start++) {
  setTimeout(function () {
    alert('hello');
  }, 3000);
}

只有第一种情况是正确的:显示后alert('hi'),它会等待 3 秒然后alert('hello')会显示,然后alert('hello')会不断重复。

我想要的是 afteralert('hello')显示 3 秒后alert('hi'),第二次需要等待 3 秒alert('hello'),依此类推。

6个回答

setTimeout()函数是非阻塞的,将立即返回。因此,您的循环将非常快速地迭代,并且会快速连续地启动 3 秒超时触发。这就是为什么您的第一个警报会在 3 秒后弹出,而其余所有警报会毫无延迟地连续出现。

你可能想使用这样的东西:

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

您还可以通过使用自调用函数将迭代次数作为参数传递来整理它:

(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

@vsync 调查 Object.keys()
2021-02-10 15:56:44
使用递归来实现它最终不会受到堆栈溢出的影响吗?如果您想做一百万次迭代,那么实现这一点的更好方法是什么?也许 setInterval 然后清除它,就像下面 Abel 的解决方案?
2021-02-18 15:56:44
当像for in循环一样迭代对象时,这将如何工作
2021-02-24 15:56:44
@joey 你setTimeoutsetInterval. 调用回调时会隐式销毁超时。
2021-03-03 15:56:44
@Adam:我的理解是,由于 setTimeout 是非阻塞的,这不是回避 - 每次 setTimeout 后堆栈窗口都会关闭,并且只有一个 setTimeout 等待执行......对吗?
2021-03-09 15:56:44

由于 ES7 有一个更好的方法来等待循环:

// Returns a Promise that resolves after "ms" Milliseconds
const timer = ms => 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();

当引擎到达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)); }

MDN 上的参考

虽然NodeJS和现代浏览器现在支持 ES7,但您可能希望使用BabelJS 对其进行转译,以便它可以在任何地方运行。

这对我来说可以。我只是想问一下,如果我想中断循环,在使用 await 时我该怎么做?
2021-02-12 15:56:44
这是迄今为止最好的解决方案,应该是公认的答案。接受的答案是hacky,不应用于任何事情。
2021-02-12 15:56:44
这仍然只会创建各种计时器,并且它们会在不同的时间而不是按顺序解决?
2021-02-22 15:56:44
@sachinbreak;也许?
2021-02-27 15:56:44
感谢您提供此解决方案。使用所有现有的控制结构并且不需要发明延续是很好的。
2021-02-28 15:56:44

如果使用 ES6,您可以使用 for 循环来实现此目的:

for (let i = 1; i < 10; i++) {
  setTimeout(function timer() {
    console.log("hello world");
  }, i * 3000);
}

i为每次迭代声明,这意味着超时是它在 + 1000 之前的状态。这样,传递给setTimeout的正是我们想要的。

setTimeout 调用i*3000在循环内同步计算参数的值,并按setTimeout传递给它的使用let是可选的,与问答无关。
2021-02-18 15:56:44
@Flame_Phoenix 提到此代码存在问题。基本上,在第一次通过时,您创建计时器,然后立即一次又一次地重复循环,直到循环以条件 ( i < 10)结束,因此您将有多个计时器并行工作,从而创建内存分配,并且在大量迭代时情况更糟。
2021-02-18 15:56:44
谢谢!我自己不会想到这种方法。实际块范围。设想...
2021-02-19 15:56:44
我相信这与stackoverflow.com/a/3583795/1337392 中描述的答案具有相同的内存分配问题
2021-02-22 15:56:44
@Flame_Phoenix 什么内存分配问题?
2021-03-06 15:56:44

尝试这样的事情:

var i = 0, howManyTimes = 10;

function f() {
  console.log("hi");
  i++;
  if (i < howManyTimes) {
    setTimeout(f, 3000);
  }
}

f();

谢谢,你让我开心!
2021-02-13 15:56:44

另一种方法是乘以超时时间,但请注意,这与 sleep 不同循环后的代码会立即执行,只有回调函数的执行才会被推迟。

for (var start = 1; start < 10; start++)
    setTimeout(function () { alert('hello');  }, 3000 * start);

第一个超时将设置为3000 * 1,第二个为3000 * 2,依此类推。

@Flame_Phoenix 这是不好的做法,因为程序将为每个循环保留一个计时器,所有计时器同时运行。所以如果有1000次迭代,一开始就会有1000个定时器同时运行。
2021-02-12 15:56:44
值得指出的是,start使用此方法无法在函数内部可靠地使用
2021-02-20 15:56:44
为什么这是一个不好的做法,为什么它有内存分配问题?这个答案是否遇到同样的问题?stackoverflow.com/a/36018502/1337392
2021-02-27 15:56:44
不好的做法 - 不必要的内存分配。
2021-03-06 15:56:44
为创造力点赞,但这是糟糕的做法。:)
2021-03-06 15:56:44