Как добавить задержку в цикле JavaScript? - PullRequest
298 голосов
/ 27 августа 2010

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

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

alert('hi');

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

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

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

Ответы [ 24 ]

675 голосов
/ 27 августа 2010

Функция 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
62 голосов
/ 27 августа 2010

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

var i = 0, howManyTimes = 10;
function f() {
    alert( "hi" );
    i++;
    if( i < howManyTimes ){
        setTimeout( f, 3000 );
    }
}
f();
51 голосов
/ 15 марта 2016

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

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

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

25 голосов
/ 10 июня 2017

Поскольку 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();

Ссылка на MDN

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

Transpiled

21 голосов
/ 27 августа 2010

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

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

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

15 голосов
/ 27 августа 2010

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

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

14 голосов
/ 06 июня 2013

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

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

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

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

7 голосов
/ 07 октября 2015

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

4 голосов
/ 06 ноября 2015

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