Как добавить задержку в цикл JavaScript?

Я хотел бы добавить задержку/сон внутри while петли:

Я пробовал так:

alert('hi');

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

истинен только первый сценарий: после показа alert('hi'), он будет ждать в течение 3 секунд, то alert('hello') будет отображаться, но затем alert('hello') будет многократно постоянно.

то, что я хотел бы, это после alert('hello') отображается через 3 секунды после alert('hi') тогда ему нужно подождать 3 секунды во второй раз alert('hello') и так далее.

22 ответов


на 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
      alert('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 () {   
      alert('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

попробуйте что-то вроде этого:

var i = 0, howManyTimes = 10;
function f() {
    alert( "hi" );
    i++;
    if( i < howManyTimes ){
        setTimeout( f, 3000 );
    }
}
f();

если вы используете ES6, вы можете использовать let достижения:

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

что let объявляется i для каждого шаг, а не петли. Таким образом, то, что передается setTimeout - это именно то, что мы хотим.


другой способ-умножить время до таймаута, но обратите внимание, что это не люблю спать. Код после того, как цикл будет выполнен немедленно, только выполнение функции обратного вызова откладывается.

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

первый тайм-аут будет установлен до 3000 * 1, второй до 3000 * 2 и так далее.


Я думаю, тебе нужно что-то вроде этого:

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 до закрытия предупреждения. Это может быть больше кода, чем вы просили, но это надежное многоразовое решение.


Я бы, вероятно, использовать 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);

Это будет работать

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

попробуйте эту скрипку: https://jsfiddle.net/wgdx8zqq/


Так как ES7 есть лучший способ ждут петли:

function timer(ms) {
 return new Promise(res => setTimeout(res, ms));
}

async function load () {
  for (var i = 0; i < 3; i++) {
    console.log(i);
    await timer(3000);
  }
}

load();

ссылка: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

обратите внимание, что ES7 редко поддерживается сегодня, поэтому вам нужно транспилировать с Babel, чтобы использовать его везде.

Transpiled


в ES6 (ECMAScript 2015) Вы можете выполнять итерацию с задержкой с помощью генератор и интервал.

генераторы, новая функция 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, это самый элегантный способ достичь цикла с задержкой (на мой взгляд).


Я делаю это с сине 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>

просто подумал, что я отправлю свои два цента здесь. Эта функция выполняет итеративный цикл с задержкой. См.этот 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);
}

Вы можете использовать RxJS интервал оператор. Интервал испускает целое число через каждые x секунд, и take Is указывает количество раз, когда он должен испускать числа

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>

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

модифицированная версия ответа Даниэля Вассалло, с переменными, извлеченными в параметры, чтобы сделать функцию более многоразовой:

сначала определим некоторые существенные переменные:

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

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

вот как я создал бесконечный цикл с задержкой, которая прерывается при определенном условии:

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

ключ здесь состоит в том, чтобы создать новое обещание, которое разрешается таймаутом, и ждать его разрешения.

очевидно, вам нужна поддержка async/await для этого. Работает в узле 8.


для общего использования " забудьте обычные циклы "и используйте эту комбинацию" 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): все они начнут в то же время "три бла бла бла начнут отсчет в тот же момент", поэтому сделайте другой тайм-аут, чтобы организовать выполнение.

PS 2: пример для цикла синхронизации, но для циклов реакции вы можете использовать события, обещание асинхронного ожидания ..


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

вот функция, которую я использую для цикла над массивом:

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
}

этот скрипт работает для большинства вещей

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

var timer, i = 10;
function myLoop () { //  create a loop function
  timer = setTimeout(function () {   
    document.getElementById("demo").innerHTML = i;
   
    i--;                    
    if (i >= 0) {            
      myLoop();              
    } else {               
      clearTimeout(timer); // clear timeout
      document.getElementById("demo").innerHTML = "DOARRRR ..";
    }
   
  }, 1000);
}

myLoop();
<p id="demo">count</p>

попробуйте это...

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

попробуй такое

//the code will execute in 1 3 5 7 9 seconds later
function exec(){
  for(var i=0;i<5;i++){
   setTimeout(function(){
     console.log(new Date());   //It's you code
   },(i+i+1)*1000);
  }
}