Как добавить задержку в цикле 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') и так далее.

Ответ 1

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

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

(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

Ответ 2

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

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

Ответ 3

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

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

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

Ответ 4

Начиная с 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();

Когда двигатель достигает части 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)); }

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

Я бы использовал 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);

Ответ 8

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

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

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

Ответ 9

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

Генераторы, новая функция ECMAScript 6, являются функциями, которые могут быть приостановлен и возобновлен. Вызов genFunc не выполняет его. Вместо этого возвращает так называемый объект-генератор, который позволяет нам управлять genFuncs выполнение. 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;
  }
}

Ответ 10

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

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

Ответ 12

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

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

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

Ответ 14

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

Ответ 15

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

  // Now continuously check the app status until it 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. Работает в Node 8.

Ответ 16

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

Ответ 17

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

Ответ 18

Насколько мне известно, функция setTimeout вызывается асинхронно. Что вы можете сделать, это обернуть весь цикл в асинхронную функцию и ждать Promise, который содержит setTimeout, как показано ниже:

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

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

Ответ 19

Вы делаете это:

alert('hi')
let start = 1
setTimeout(function(){
  let interval = setInterval(function(){
    if(start == 10) clearInterval(interval)
    start++
    alert('hello')
  }, 3000)
}, 3000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Ответ 20

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

Ответ 21

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

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
}

Ответ 22

Этот script работает для большинства вещей

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

Ответ 23

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>

Ответ 24

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

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

Ответ 25

Простая реализация показа фрагмента текста каждые две секунды, пока работает цикл.

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

Ответ 26

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

//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 you code
   },(i+i+1)*1000);
  }
}