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

How do I add a delay in a JavaScript loop?

提问人:olidev 提问时间:8/27/2010 最后编辑:halferolidev 更新时间:6/19/2023 访问量:771094

问:

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

我是这样试的:

alert('hi');

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

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

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

JavaScript 循环 休眠

评论

0赞 mhndlsz 7/1/2021
for(var i=0; i < 5; i++){delayLoop(i)};函数 delayLoop(i){setTimeout(function(){console.log('以 1 秒延迟打印'),(i*1000)}
0赞 Bilal Khursheed 12/29/2021
const setTimeOutFn= async()=>{ for(var start = 0; start < 3; start++) { await new Promise( async(res , rej )=>{ setTimeout(() => { console.log('hello', start); res() }, 3000); }) } }
0赞 marko-36 12/6/2022
在每个循环中设置一个 timeOut,只是使用不同的值,可能不是一个好主意。这是一个无聊的单行代码,它实际上使用 promise (async/await) 停止代码执行:stackoverflow.com/questions/3583724/...

答:

965赞 Daniel Vassallo 8/27/2010 #1

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

评论

47赞 Adam 6/25/2014
使用递归来实现这一点最终会不会受到堆栈溢出的影响?如果你想做一百万次迭代,有什么更好的方法来实现这一点?也许setInterval,然后清除它,就像下面的 Abel 解决方案一样?
14赞 Joe 9/3/2015
@Adam:我的理解是,由于 setTimeout 是非阻塞的,这不是 recusion - 堆栈窗口在每次 setTimeout 后关闭,并且只有一个 setTimeout 等待执行......右?
4赞 vsync 9/15/2015
当迭代一个像循环这样的对象时,这将如何工作?for in
1赞 Braden Best 8/13/2016
@vsync 调查Object.keys()
1赞 cdhowie 3/2/2018
@joey 您与 .调用回调时,超时会隐式销毁。setTimeoutsetInterval
82赞 cji 8/27/2010 #2

尝试这样的事情:

var i = 0, howManyTimes = 10;

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

f();

评论

0赞 vsync 3/19/2022
const run = (t, d) => {console.log(t); t > 1 && setTimeout(run, d, --t, d)}
0赞 Vincent 7/15/2023
我喜欢这个答案,因为它有效地创建了一个同步循环,并且最容易理解。
25赞 Felix Kling 8/27/2010 #3

另一种方法是将超时时间相乘,但请注意,这与睡眠不同。循环后的代码将立即执行,只有回调函数的执行被延迟。

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

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

评论

3赞 DBS 6/24/2015
值得指出的是,使用此方法无法可靠地在函数内部使用。start
2赞 Alexander Trakhimenok 10/7/2015
不好的做法 - 不必要的内存分配。
0赞 Salivan 6/28/2016
为创造力投票,但这是该死的坏做法。:)
2赞 Flame_Phoenix 7/21/2016
为什么这是一个不好的做法,为什么它有内存分配问题?这个答案是否存在同样的问题?stackoverflow.com/a/36018502/1337392
1赞 Joakim 8/10/2018
@Flame_Phoenix这是不好的做法,因为程序将为每个循环保留一个计时器,所有计时器同时运行。因此,如果有 1000 次迭代,则开始时将有 1000 个计时器同时运行。
18赞 BGerrissen 8/27/2010 #4

我认为你需要这样的东西:

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;
    }
}

测试代码:

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();

注意:使用警报会停止 javascript 执行,直到您关闭警报。 它可能比您要求的代码多,但这是一个强大的可重用解决方案。

16赞 Abel Terefe 6/6/2013 #5

我可能会使用,如下所示:setInterval

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);

评论

3赞 Abdul Jabbar 2/24/2014
SetTimeout 比 settinterval 好得多。谷歌一下,你就会知道
17赞 Marcs 3/21/2016
我用谷歌搜索了一下,什么也没找到,为什么setInterval不好?你能给我们一个链接吗?还是举个例子?谢谢
0赞 Mateen Ulhaq 11/21/2017
我想关键是即使在发生某些错误或阻塞的情况下,也会不断生成“线程”。SetInterval()
-1赞 MicroSharkBaby 10/24/2013 #6
/* 
  Use Recursive  and setTimeout 
  call below function will run loop loopFunctionNeedCheck until 
  conditionCheckAfterRunFn = true, if conditionCheckAfterRunFn == false : delay 
  reRunAfterMs miliseconds and continue loop
  tested code, thanks
*/

function functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn,
 loopFunctionNeedCheck) {
    loopFunctionNeedCheck();
    var result = conditionCheckAfterRunFn();
    //check after run
    if (!result) {
        setTimeout(function () {
            functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn, loopFunctionNeedCheck)
        }, reRunAfterMs);
    }
    else  console.log("completed, thanks");    
            //if you need call a function after completed add code call callback in here
}

//passing-parameters-to-a-callback-function
// From Prototype.js 
if (!Function.prototype.bind) { // check if native implementation available
    Function.prototype.bind = function () {
        var fn = this, args = Array.prototype.slice.call(arguments),
            object = args.shift();
        return function () {
            return fn.apply(object,
              args.concat(Array.prototype.slice.call(arguments)));
        };
    };
}

//test code: 
var result = 0; 
console.log("---> init result is " + result);
var functionNeedRun = function (step) {           
   result+=step;    
       console.log("current result is " + result);  
}
var checkResultFunction = function () {
    return result==100;
}  

//call this function will run loop functionNeedRun and delay 500 miliseconds until result=100    
functionRepeatUntilConditionTrue(500, checkResultFunction , functionNeedRun.bind(null, 5));

//result log from console:
/*
---> init result is 0
current result is 5
undefined
current result is 10
current result is 15
current result is 20
current result is 25
current result is 30
current result is 35
current result is 40
current result is 45
current result is 50
current result is 55
current result is 60
current result is 65
current result is 70
current result is 75
current result is 80
current result is 85
current result is 90
current result is 95
current result is 100
completed, thanks
*/

评论

8赞 Mark Walters 11/26/2013
你的函数名称太可怕了,这就是这段代码如此难以阅读的主要原因。
5赞 Dave Bryand 2/2/2015 #7

我用 Bluebird 和递归来做到这一点。Promise.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>

评论

0赞 ggorlen 7/17/2022
适用于本机 setTimeout 而不是 bluebeard,从而节省了依赖关系。不过我不会在这里使用。如果添加了更多在调用中使用的逻辑,例如,为数组编制索引,则它可能具有意外值。此外,它实际上不是递归的;调用堆栈在子调用发生之前清除。它只是碰巧是相同的功能。你可以用 0 和几百万的超时/延迟来证明这一点i -= 1ii
-1赞 PJeremyMalouf 6/2/2015 #8

这是我用来遍历数组的函数:

function loopOnArrayWithDelay(theArray, delayAmount, i, theFunction, onComplete){

    if (i < theArray.length && typeof delayAmount == 'number'){

        console.log("i "+i);

        theFunction(theArray[i], i);

        setTimeout(function(){

            loopOnArrayWithDelay(theArray, delayAmount, (i+1), theFunction, onComplete)}, delayAmount);
    }else{

        onComplete(i);
    }
}

你像这样使用它:

loopOnArrayWithDelay(YourArray, 1000, 0, function(e, i){
    //Do something with item
}, function(i){
    //Do something once loop has completed
}
10赞 Itay Radotzki 10/7/2015 #9

在 ES6 (ECMAScript 2015) 中,你可以使用 generator 和 interval 进行延迟迭代。

生成器是 ECMAScript 6 的一个新特性,是可以 暂停并恢复。调用 genFunc 不会执行它。相反,它 返回一个所谓的 generator 对象,它让我们可以控制 genFunc 的 执行。genFunc() 最初在其开头被挂起 身体。方法 genObj.next() 继续执行 genFunc, 直到下一个产量。(探索 ES6)


代码示例:

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;
  }
}

因此,如果您使用的是 ES6,这是实现延迟循环的最优雅方式(在我看来)。

3赞 Dom Slee 11/6/2015 #10

我以为我也会在这里发布我的两分钱。此函数运行具有延迟的迭代循环。看到这个 jsfiddle。功能如下:

function timeout(range, time, callback){
    var i = range[0];                
    callback(i);
    Loop();
    function Loop(){
        setTimeout(function(){
            i++;
            if (i<range[1]){
                callback(i);
                Loop();
            }
        }, time*1000)
    } 
}

例如:

//This function prints the loop number every second
timeout([0, 5], 1, function(i){
    console.log(i);
});

相当于:

//This function prints the loop number instantly
for (var i = 0; i<5; i++){
    console.log(i);
}
1赞 Jasdeep Khalsa 3/10/2016 #11

    var startIndex = 0;
    var data = [1, 2, 3];
    var timeout = 1000;

    function functionToRun(i, length) {
      alert(data[i]);
    }

    (function forWithDelay(i, length, fn, delay) {
      setTimeout(function() {
        fn(i, length);
        i++;
        if (i < length) {
          forWithDelay(i, length, fn, delay);
        }
      }, delay);
    })(startIndex, data.length, functionToRun, timeout);

Daniel Vassallo 答案的修改版本,将变量提取到参数中,使函数更可重用:

首先,让我们定义一些基本变量:

var startIndex = 0;
var data = [1, 2, 3];
var timeout = 3000;

接下来,您应该定义要运行的函数。这将传递 i,循环的当前索引和循环的长度,以备不时之需:

function functionToRun(i, length) {
    alert(data[i]);
}

自动执行版本

(function forWithDelay(i, length, fn, delay) {
   setTimeout(function () {
      fn(i, length);
      i++;
      if (i < length) {
         forWithDelay(i, length, fn, delay); 
      }
  }, delay);
})(startIndex, data.length, functionToRun, timeout);

功能版本

function forWithDelay(i, length, fn, delay) {
   setTimeout(function () {
      fn(i, length);
      i++;
      if (i < length) {
         forWithDelay(i, length, fn, delay); 
      }
  }, delay);
}

forWithDelay(startIndex, data.length, functionToRun, timeout); // Lets run it

评论

0赞 Sundara Prabu 5/30/2017
不错的一个,如何在没有全局变量的情况下将数据传递给函数
127赞 Saket Mehta 3/16/2016 #12

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

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

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

评论

3赞 Flame_Phoenix 7/21/2016
我相信这与 stackoverflow.com/a/3583795/1337392 中描述的答案具有相同的内存分配问题
2赞 4castle 7/21/2017
@Flame_Phoenix 哪些内存分配问题?
3赞 traktor 11/21/2018
setTimeout 调用在循环中同步计算参数的值,并将其传递给 by value。的用法是可选的,与问题和答案无关。i*3000setTimeoutlet
2赞 XCanG 7/18/2019
@Flame_Phoenix提到此代码中存在问题。基本上,在第一次通过时,您创建计时器,然后立即一次又一次地重复循环,直到循环通过条件 () 结束,因此您将有多个计时器并行工作,从而创建内存分配,并且在大量迭代时情况更糟。i < 10
1赞 Jonas Wilms 8/12/2019
@traktor53是完全正确的,只有当您打算setTimeout 回调中使用它时,使用 here 才更好。你可能想在你的回答中指出这一点let
0赞 ZomoXYZ 3/19/2016 #13

此脚本适用于大多数事情

function timer(start) {
    setTimeout(function () { //The timer
        alert('hello');
    }, start*3000); //needs the "start*" or else all the timers will run at 3000ms
}

for(var start = 1; start < 10; start++) {
    timer(start);
}
2赞 Vlad Bezden 6/24/2016 #14

您可以使用 RxJS 间隔运算符。 每秒发出一个整数,并指定它发出这些数字的次数。intervalxtake

Rx.Observable
  .interval(1000)
  .take(10)
  .subscribe((x) => console.log(x))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.lite.min.js"></script>

20赞 Gsvp Nagaraju 4/5/2017 #15

这将起作用

for (var i = 0; i < 10; i++) {
  (function(i) {
    setTimeout(function() { console.log(i); }, 100 * i);
  })(i);
}

试试这个小提琴: https://jsfiddle.net/wgdx8zqq/

评论

1赞 Eddie 4/11/2018
不过,这确实会同时触发所有超时调用
0赞 ArifMustafa 10/20/2018
我唯一要说的是,我已经以这种方式破解了,使用了,但让它起作用是一些不同的场景,对你竖起大拇指..!$.Deferred
307赞 Jonas Wilms 6/11/2017 #16

由于 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();

当引擎到达部件时,它会设置超时并停止异步函数的执行。然后,当超时完成时,执行将在此时继续。这非常有用,因为您可以延迟 (1) 嵌套循环,(2) 有条件地延迟,(3) 嵌套函数:await

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 对其进行转译,以便它在任何地方运行。

评论

0赞 Sachin Shah 8/7/2018
它对我来说很好用。我只想问,如果我想打破循环,在使用 await 时该怎么做?
1赞 Jonas Wilms 8/7/2018
@sachin也许吧?break;
1赞 Gus 12/15/2019
感谢您提供此解决方案。使用所有现有的控制结构是很好的,而不需要发明延续。
17赞 Krokodil 6/20/2021
这是迄今为止最好的解决方案,应该是公认的答案。公认的答案是骇人听闻的,不应该用于任何事情。
3赞 ggorlen 7/17/2022
很好的解决方案,但为了吹毛求疵,我会调用函数或而不是.类是名词,函数是动词。他们做某事或采取行动,而不是代表一件事。sleepwaittimer
0赞 user6269864 9/28/2017 #17

以下是我如何创建一个无限循环,其延迟在特定条件下中断:

  // Now continuously check the app status until it's completed, 
  // failed or times out. The isFinished() will throw exception if
  // there is a failure.
  while (true) {
    let status = await this.api.getStatus(appId);
    if (isFinished(status)) {
      break;
    } else {
      // Delay before running the next loop iteration:
      await new Promise(resolve => setTimeout(resolve, 3000));
    }
  }

这里的关键是创建一个通过超时解析的新 Promise,并等待其解析。

显然,您需要 async/await 支持。在节点 8 中工作。

0赞 Mohamed Abulnasr 12/1/2017 #18

对于常用的“忘记普通循环”并使用“setInterval”的组合,包括“setTimeOut”:像这样(来自我的实际任务)。

        function iAsk(lvl){
            var i=0;
            var intr =setInterval(function(){ // start the loop 
                i++; // increment it
                if(i>lvl){ // check if the end round reached.
                    clearInterval(intr);
                    return;
                }
                setTimeout(function(){
                    $(".imag").prop("src",pPng); // do first bla bla bla after 50 millisecond
                },50);
                setTimeout(function(){
                     // do another bla bla bla after 100 millisecond.
                    seq[i-1]=(Math.ceil(Math.random()*4)).toString();
                    $("#hh").after('<br>'+i + ' : rand= '+(Math.ceil(Math.random()*4)).toString()+' > '+seq[i-1]);
                    $("#d"+seq[i-1]).prop("src",pGif);
                    var d =document.getElementById('aud');
                    d.play();                   
                },100);
                setTimeout(function(){
                    // keep adding bla bla bla till you done :)
                    $("#d"+seq[i-1]).prop("src",pPng);
                },900);
            },1000); // loop waiting time must be >= 900 (biggest timeOut for inside actions)
        }

PS:了解(setTimeOut)的真实行为:它们都会同时开始“三个bla bla bla将在同一时刻开始倒计时”,所以做一个不同的超时来安排执行。

PS 2:定时循环的例子,但对于反应循环,你可以使用事件,promise async await 。

-1赞 Boginaathan M 1/23/2018 #19

<!DOCTYPE html>
<html>
<body>

<button onclick="myFunction()">Try it</button>

<p id="demo"></p>

<script>
function myFunction() {
    for(var i=0; i<5; i++) {
    	var sno = i+1;
       	(function myLoop (i) {          
             setTimeout(function () {   
             	alert(i); // Do your function here 
             }, 1000*i);
        })(sno);
    }
}
</script>

</body>
</html>

评论

2赞 Hexfire 1/23/2018
请始终至少为您的代码片段提供简要说明,至少让其他人确保您解决了该问题。
2赞 WhatsThePoint 1/23/2018
不鼓励仅使用代码答案,因为它们不会为未来的读者提供太多信息,请对您所写的内容提供一些解释
3赞 otboss 2/24/2019 #20

据我所知,该函数是异步调用的。您可以做的是将整个循环包装在异步函数中,并等待包含 setTimeout 的函数,如下所示:setTimeoutPromise

var looper = async function () {
  for (var start = 1; start < 10; start++) {
    await new Promise(function (resolve, reject) {
      setTimeout(function () {
        console.log("iteration: " + start.toString());
        resolve(true);
      }, 1000);
    });
  }
  return true;
}

然后你像这样调用运行它:

looper().then(function(){
  console.log("DONE!")
});

请花一些时间好好了解异步编程。

0赞 Ali Azhar 10/11/2019 #21
   let counter =1;
   for(let item in items) {
        counter++;
        setTimeout(()=>{
          //your code
        },counter*5000); //5Sec delay between each iteration
    }

评论

1赞 niry 10/23/2020
这忽略了在循环中延迟的请求。只是以 5 秒的间隔设置一系列事件(不妨使用 )。为了更好地理解问题,请在点击“确定”之前使用并等待 5 秒钟。 下一个警报将立即显示,没有延迟。setIntervalalert
4赞 Tabish 3/4/2020 #22

在 ES6 中,您可以执行以下操作:

 for (let i = 0; i <= 10; i++){       
     setTimeout(function () {   
        console.log(i);
     }, i*3000)
 }

在 ES5 中,您可以执行以下操作:

for (var i = 0; i <= 10; i++){
   (function(i) {          
     setTimeout(function () {   
        console.log(i);
     }, i*3000)
   })(i);  
 }

原因是,允许您声明仅限于块语句或使用它的表达式的作用域的变量,这与关键字不同,关键字全局定义变量,或局部定义整个函数,而不管块作用域如何。letvar

1赞 Shirantha Madusanka 4/26/2020 #23

试试这个

 var arr = ['A','B','C'];
 (function customLoop (arr, i) {
    setTimeout(function () {
    // Do here what you want to do.......
    console.log(arr[i]);
    if (--i) {                
      customLoop(arr, i); 
    }
  }, 2000);
})(arr, arr.length);

结果

A // after 2s
B // after 2s
C // after 2s

评论

0赞 ggorlen 7/17/2022
多解释一点就好了。为什么我应该尝试相对于这里其他 40 个答案之一?它是如何工作的,它有什么优点?谢谢。
3赞 niry 8/20/2020 #24

除了 10 年前公认的答案之外,使用更现代的 Javascript 还可以使用 // 或生成器函数来实现正确的行为。(其他答案中建议的错误行为是设置一系列 3 秒警报,无论是否“接受”或完成手头的任务)asyncawaitPromise()alert()

用//:asyncawaitPromise()

alert('hi');

(async () => {
  for(let start = 1; start < 10; start++) {
    await new Promise(resolve => setTimeout(() => {
      alert('hello');
      resolve();
    }, 3000));
  }
})();

使用生成器函数:

alert('hi');

let func;

(func = (function*() {
  for(let start = 1; start < 10; start++) {
    yield setTimeout(() => {
      alert('hello');
      func.next();
    }, 3000);
  }
})()).next();

20赞 4 revs, 3 users 69%Anoop #25

您可以创建一个承诺的函数。这使您能够在没有回调和熟悉的循环控制流的情况下使用 / 编写代码。sleepsetTimeoutasyncawaitfor

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

(async () => {
  for (let i = 0; i < 10; i++) {
    console.log(i);
    await sleep(1000);
  }

  console.log("done");
})();

在 Node 中,您可以使用来避免承诺步骤(如果旧版 Node 不支持该功能,则上述代码也同样有效):timers/promises

const {setTimeout: sleep} = require("timers/promises");

// same code as above

无论如何,由于 JS 是单线程的,因此超时是异步的是一件好事。否则,浏览器将没有机会重新绘制 UI,从而导致用户的界面冻结。

13赞 Leon Grin 12/1/2020 #26

在我看来,在循环中添加延迟的更简单、最优雅的方法是这样的:

names = ['John', 'Ana', 'Mary'];

names.forEach((name, i) => {
 setTimeout(() => {
  console.log(name);
 }, i * 1000);  // one sec interval
});

评论

1赞 Omar Omeiri 12/6/2022
这实际上很糟糕。forEach 不会等待 setTimeout。您只需创建数组中元素的超时数量即可。对于 3 元素数组,这是可以的。但是,如果您有数百甚至数千台计算机,则计算机可能会着火。
4赞 Shannarra 2/18/2021 #27

无功能解决方案

我来晚了一点,但是有一个不使用任何功能的解决方案:

alert('hi');

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

评论

0赞 niry 12/9/2021
这将以 3 秒的间隔安排 10 个警报,而不是在 alert() 清除后等待 3 秒。如果第一个 alert() 在 30 秒内未被清除,则其余的 alert() 之间将没有停顿。
0赞 vsync 3/19/2022
这个确切的解决方案已经在 2010 年在这里给出了(由 Felix Kling 提供)。
0赞 Oushima 6/12/2021 #28
const autoPlayer = (arr = [1, 2, 3, 4, 5]) => {
  // Base case:
  if (arr.length < 1) return

  // Remove the first element from the array.
  const item = arr.shift()

  // Set timout 
  setTimeout(() => {
    console.log('Hello, world!', item)  // Visualisation.
    autoPlayer() // Call function again.
  }, 1000) // Iterate every second.
}

嘿,我知道这篇文章很旧,但是这段代码会“循环”并使用递归方法为其添加延迟。我不认为你可以“实际上”根据阅读其他人的各种评论来延迟循环本身的迭代。也许这可以帮助某人!基本上,该函数接受一个数组(在本例中)。在每次迭代中,都会调用 Javascript 方法。当函数的计时器到期时,函数会无限期地再次调用自身,但在每次调用时,数组都会变小,直到达到基本情况。我希望这可以帮助其他人。setTimeoutsetTimeout

评论

0赞 ggorlen 7/17/2022
这实际上不是递归的。在回调运行之前,必须清除调用堆栈。setTimeout
0赞 Oushima 7/22/2022
寄件人:techterms.com/definition/recursive_function 引言:递归函数是在执行过程中调用自身的函数。该过程可能会重复几次,输出结果和每次迭代的结束。- 此函数调用自身。我看不出有任何理由说明为什么这不是递归的。
0赞 ggorlen 7/22/2022
从代码的角度来看,这是正确的,但从应用程序的角度来看,它不是递归的,因为调用堆栈完全清除。在大多数递归函数中,调用会等待递归子帧将控制权返回给父帧,然后才能自行解析,这不会发生在这里。这实际上是一件好事,因为与 JS 中的传统递归同步函数相比,你不能用这段代码破坏堆栈。
7赞 marko-36 9/3/2022 #29

非常简单的单行解决方案,具有实际的异步等待延迟(没有排队的 setTimeout):

await new Promise((resolve) => {setTimeout(() => {resolve(true)}, 190)});

异步函数中的实际延迟,作为通常使用具有不同超时间隔的多个 s 的替代方法,这可能会弄乱内存。在此片段的情况下,延迟 190 毫秒。setTimeout

例:

  • 在 100 个循环中的每一个循环中,它都为 a 到 。awaitnew Promiseresolve
  • 这仅在 190 毫秒后“允许”后才会发生。在此之前,代码被 async-await / pending 阻止。setTimeoutPromise

async function delayTest() {
  for (let i=0; i<100; i++) {
    await new Promise((resolve) => {setTimeout(() => {document.write(`${i} `); resolve(true)}, 190)});
  }
};

delayTest()

0赞 Hani 6/19/2023 #30

一个简单但不是很优雅的解决方案是嵌套第二个循环,该循环将充当计时器(本质上,嵌套循环将保持线程)。如果你正在写一些临时的东西,这是一个肮脏的解决方法。

for(i=0;i<arr.length;i++){
   //you logic here

   //this will act as a timer
   for(j=0;j<25000;j++) console.log("")
}

上周我不得不从一个网站下载大约 40 个文件,所以我写了一个简短的 JS 脚本,通过 Chrome 的控制台执行,嵌套循环技巧在延迟执行方面做得很好。