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

我这样尝试过:

alert('hi');

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


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

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

#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 




评论


难道不会使用递归来最终实现堆栈溢出吗?如果您想进行一百万次迭代,哪种更好的方法来实现呢?也许先设置setInterval然后再清除它,就像下面的Abel解决方案一样?

–亚当
2014年6月24日21:15



@Adam:我的理解是,由于setTimeout是非阻塞的,因此这不是回避-每次setTimeout之后堆栈窗口都会关闭,并且只有一个setTimeout等待执行...对吗?

–乔
2015年9月2日于17:10

在迭代for for in循环等对象时,这将如何工作?

– vsync
2015年9月14日20:35



@vsync查看Object.keys()

– Braden Best
16年8月12日在18:41



@joey您将setTimeout与setInterval混淆了。调用回调时,超时被隐式破坏。

– cdhowie
18 Mar 1 '18 at 18:08



#2 楼

由于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时该如何做?

– Sachin Shah
18年8月7日在11:25

@sachin break;也许?

–乔纳斯·威尔姆斯(Jonas Wilms)
18年8月7日在11:47

感谢您的解决方案。使用所有现有的控制结构并且无需发明连续性是很好的。

– Gus
19/12/14在22:08

这仍然会创建各种计时器,它们将在不同的时间而不是按顺序解决吗?

–David Yell
5月18日18:00

@JonasWilms似乎我完全想念'Run snippet'按钮:facepalm:

–David Yell
5月19日10:39

#3 楼

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



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




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

评论


谢谢!我自己不会想到这种方法。实际的块作用域。想象一下...

–索菲亚·金(Sophia Gold)
16年7月10日在1:46

我相信这与在stackoverflow.com/a/3583795/1337392中描述的答案具有相同的内存分配问题

– Flame_Phoenix
16年7月21日在10:58

@Flame_Phoenix什么内存分配问题?

– 4castle
17年7月21日在0:29

setTimeout调用在循环内部同步计算i * 3000参数的值,并将其按值传递给setTimeout。 let的用法是可选的,与问题和答案无关。

–traktor53
18年11月21日,0:58

@Flame_Phoenix提到此代码中存在问题。基本上是在第一次通过时,您创建了计时器,然后立即一次又一次地重复循环,直到循环被条件(i <10)结束为止,因此您将有多个并行工作的计时器,这会创建内存分配,并且在进行大量迭代时会更糟。

– XCanG
19年7月17日在21:09

#4 楼

试试这样的东西:



 var i = 0, howManyTimes = 10;

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

f(); 




#5 楼

另一种方法是增加超时时间,但是请注意,这不像睡眠。循环后的代码将立即执行,仅推迟执行回调函数。

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


第一个超时将设置为3000 * 1,第二个超时设置为3000 * 2,等等。

评论


值得指出的是,使用这种方法不能可靠地在函数内部使用start。

–星展银行
15年6月24日在13:17

不良做法-不必要的内存分配。

–亚历山大·特拉希梅诺克(Alexander Trakhimenok)
2015年10月7日15:37

支持创造力,但这是糟糕的做法。 :)

– Salivan
16年6月27日在18:51

为什么这是一个不好的做法,为什么它有内存分配问题?这个答案会遇到同样的问题吗? stackoverflow.com/a/36018502/1337392

– Flame_Phoenix
16年7月21日在11:00



@Flame_Phoenix是一个不好的做法,因为该程序将为每个循环保留一个计时器,所有计时器同时运行。因此,如果有1000次迭代,则开始时将同时运行1000个计时器。

–乔基姆(Joakim)
18年8月9日在18:18

#6 楼

这将起作用

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


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

评论


虽然这确实会触发几乎所有时间的所有超时调用

–艾迪
18年4月10日在19:19

我唯一说的是,我已经用这种方式破解了,使用了$ .Deferred,但是让它起作用的是一些不同的方案,谢谢!

– ArifMustafa
18-10-20在6:55

#7 楼

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

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。
代码可能比您要求的要多,但这是一个可靠的可重用解决方案。

#8 楼

我可能会使用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);


评论


SetTimeout比settinterval好得多。谷歌它,你会知道

–艾里
2014-2-23在22:26

我用谷歌搜索了一下,却什么也没发现,为什么setInterval不好?你能给我们一个链接吗?还是一个例子?谢谢

– Marcs
16-3-20在22:08

我想说的是,即使发生某些错误或阻塞,SetInterval()也会继续产生“线程”。

–玛蒂·乌尔哈克
17年11月21日1:00



#9 楼

在ES6(ECMAScript 2015)中,可以使用生成器和间隔延迟进行迭代。


生成器是ECMAScript 6的新功能,可以暂停和恢复功能。调用genFunc不会执行它。相反,它
返回一个所谓的生成器对象,使我们可以控制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,那是实现延迟循环的最优雅的方法(我认为)。

#10 楼

我使用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> 




#11 楼

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





原因是,let允许您声明限制在块语句或使用该语句的表达式范围内的变量,这与var关键字定义的变量不同全局或局部于整个函数,而不管块范围如何。

#12 楼

您可以使用RxJS间隔运算符。间隔每x秒发射一次整数,并指定要发射数字的次数




 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> 




#13 楼

只是以为我也会在这里寄两美分。此函数延迟运行迭代循环。看到这个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);
}


#14 楼




     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


评论


不错,我如何在没有全局变量的情况下将数据传递给函数

– Sundara Prabu
17年5月29日在17:54

#15 楼

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

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!")
});


请花一些时间来更好地理解异步编程。

#16 楼

只需尝试一下

 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


#17 楼

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



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




#18 楼

/* 
  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
*/


评论


您的函数名称太可怕了,这就是为什么此代码难以阅读的主要原因。

–马克·沃尔特斯(Mark Walters)
13年11月26日在15:11

#19 楼

这是我如何创建一个在一定条件下会延迟的无限循环:

  // 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,该Promise通过超时来解决,并且等待它的解决。

显然,您需要对此的异步/等待支持。在节点8中工作。

#20 楼

通常使用“忘记正常循环”,并使用“ 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:时序循环的示例,但对于反应循环,您可以使用事件,请保证异步等待..

#21 楼




 <!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> 




评论


请始终至少对您的代码段提供简短的描述,至少让其他人确保您能够解决问题。

– Hexfire
18年1月23日在7:47

代码只能回答鼓励的问题,因为它们不能为以后的读者提供太多信息,请对您的内容提供一些解释

– WhatsThePoint
18年1月23日在7:58

#22 楼

   let counter =1;
   for(let item in items) {
        counter++;
        setTimeout(()=>{
          //your code
        },counter*5000); //5Sec delay between each iteration
    }


评论


这无视请求在循环内有延迟。只需按5秒间隔设置一系列事件即可(也可以使用setInterval)。为了更好地理解问题,请使用Alert并等待5秒钟,然后再单击OK。下一个警报将立即显示,不会延迟。

–niry
10月23日6:41



#23 楼

您做到了:




 console.log('hi')
let start = 1
setTimeout(function(){
  let interval = setInterval(function(){
    if(start == 10) clearInterval(interval)
    start++
    console.log('hello')
  }, 3000)
}, 3000) 

 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> 




评论


最好使用控制台日志代替警报,关闭警报半分钟不是很有趣;)

–亨德里
19年8月23日在9:49

是的我懂了!但是请求很警惕...

– Nguyen Ba Danh-FAIC HN
19年8月29日在2:07

为什么要导入jQuery?

– Elias Soares
19-10-10在21:16

对不起...没必要..呵呵。我不知道发布内容...首先。

– Nguyen Ba Danh-FAIC HN
19-10-29在6:22

预设间隔时间的另一个答案不考虑任务警报,它不会回答问题。

–niry
10月23日6:35

#24 楼

var count = 0;

//Parameters:
//  array: []
//  fnc: function (the business logic in form of function-,what you want to execute)
//  delay: milisecond  

function delayLoop(array,fnc,delay){
    if(!array || array.legth == 0)return false;
    setTimeout(function(data){ 
        var data = array[count++];
        fnc && fnc(data);
        //recursion...
        if(count < array.length)
            delayLoop(array,fnc,delay);
        else count = 0;     
    },delay);
}


#25 楼




 function waitforme(ms)  {

return new Promise( resolve => {

    setTimeout(()=> {resolve('')} ,ms );


})


}



async function printy()  {




for (let i= 0; i < 10 ; ++i)    {


    await waitforme(1000);
    //wait for 500 milisecond

    console.log(i);


}


console.log("I Ran after the loop finished :)");
}


printy(); 




#26 楼

我认为,在循环中添加延迟的最简单,最优雅的方法是这样的:
names = ['John', 'Ana', 'Mary'];

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


#27 楼

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

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
}


#28 楼

该脚本适用于大多数情况

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


#29 楼

试试这个...

var icount=0;
for (let i in items) {
   icount=icount+1000;
   new beginCount(items[i],icount);
}

function beginCount(item,icount){
  setTimeout(function () {

   new actualFunction(item,icount);

 }, icount);
}

function actualFunction(item,icount){
  //...runs ever 1 second
 console.log(icount);
}


#30 楼

只要循环正在运行,每两秒钟显示一条文本的简单实现。

for (var i = 0; i < foo.length; i++) {
   setInterval(function(){ 
     console.log("I will appear every 2 seconds"); 
   }, 2000);
  break;
};