Подтвердить что ты не робот

JavaScript: для цикла с таймаутом

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

for(var i=0; i<10; i++) {
    console.log(i);
    //wait for 1000
}

Я нашел много решений по переполнению стека, как это:

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

Но во всех реализациях цикл сначала ожидает 3000 миллисекунд, а затем сразу выполняет весь цикл for. Есть ли способ, что каждая итерация вызывается после ожидания 1000 миллисекунд.

4b9b3361

Ответ 1

Вы можете работать с простой математикой:

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

1000ms: 0
4000ms: 1
7000мс: 2
10000 мс: 3
13000мс: 4
...


Следуя комментариям

Кажется, что ваш запрос немного размыт. если вы хотите что-то сделать после последнего таймаута, вы можете установить предел и сравнить текущий индекс:

var limit = 10
for (var i=0;i<=limit;i++) {
   (function(ind) {
       setTimeout(function(){
           console.log(ind);
           if(ind === limit){
               console.log('It was the last one');
           }
       }, 1000 + (3000 * ind));
   })(i);
}

Fiddle: http://jsfiddle.net/Tn4A7/


Думаю, я знаю, чего вы хотите...

и просто делать

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

Ответ 2

Не выполняйте функции внутри циклов, вместо этого:

(function fiveSeconds  (n) {

  if (n < 5) setTimeout(function () {  
    fiveSeconds ( n ); // Redo if n < 5 (and pass n)
  }, 1000);
  
  console.log( n++ );

} (0)); // Initialize. n is 0

Ответ 3

почему бы не использовать что-то вроде этого:

var i = 0
var id = window.setInterval(function(){
    if(i >= 10) {
        clearInterval(id);
        return;
    }

    console.log(i);
    i++;
}, 1000)

Ответ 4

Это работает:

function initiateTimeOut(i) {
  setTimeout(function() { doStuff(i) }, 30);
}
function doStuff(i) {
    console.log(i);
    i++;
    if (i <= 10) {
        initiateTimeOut(i); 
    }
}

initiateTimeOut(0);

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

Пример в скрипте: http://jsfiddle.net/My7Zg/


Или, даже короче (http://jsfiddle.net/My7Zg/1/):

function customLoop(i) {
    console.log(i);
    i++;
    if (i<=10) {setTimeout(function(){customLoop(i);},1000);}
}
customLoop(0);

Ответ 5

for (var i=0;i<=10;i++) {
   (function(ind) {
       setTimeout(function(){console.log((ind + 1)*1000, ':', ind);}, 1000 * (ind+1) );
   })(i);
}

Вывод:

1000 : 0
2000 : 1
3000 : 2
4000 : 3
5000 : 4
6000 : 5
7000 : 6
8000 : 7
9000 : 8
10000 : 9
11000 : 10

РАБОЧИЙ ДЕМО

Ответ 6

Вы можете подойти к своей ситуации двумя способами.

  • Вы можете немедленно запланировать целую цепочку вызовов setTimeout() с различными моментами, чтобы они выполнялись в нужное время в будущем (другие ответы здесь иллюстрируют, как это сделать).

  • Вы можете выполнить первую итерацию, запланировать следующую итерацию и выполнить следующий график итерации после этого, пока не закончите нужное количество итераций. Это в конечном счете немного более масштабируемо, чем установка большого количества вызовов setTimeout() и дает вам больше свободы разветвления/логики, потому что вы контролируете, что происходит дальше после каждой итерации.

Этот второй вариант с использованием функции общего назначения полезности будет выглядеть так:

// utility function to call a callback numTimes, 
// separated by delay milliseconds
function runIteration(fn, numTimes, delay) {
    var cnt = 0;
    function next() {
        // call the callback and stop iterating if it returns false
        if (fn(cnt) === false) return;
        ++cnt;
        // if not finished with desired number of iterations,
        // schedule the next iteration
        if (cnt < numTimes) {
            setTimeout(next, delay);
        }
    }
    // start first iteration
    next();

}

Итак, чтобы выполнить оператор консоли, вы сделаете следующее:

runIteration(function(i) {
    console.log(i);
}, 10, 1000);

Рабочая демонстрация: http://jsfiddle.net/jfriend00/HqCZ3/

Это также можно расширить с помощью функции 2-го обратного вызова, которая была вызвана, когда итерация была завершена (полезна в некоторых случаях), или она может вернуть обещание, которое разрешается, когда итерации завершены.

Здесь какая версия, которая возвращает обещание, будет выглядеть так: http://jsfiddle.net/jfriend00/XtJ69/

// utility function to call a callback numTimes, 
// separated by delay milliseconds
function runIteration(fn, numTimes, delay) {
    var d = $.Deferred();
    var cnt = 0;

    function end() {
        d.resolve();
    }

    function next() {
        // call the callback and stop iterating if
        // it returns false
        if (fn(cnt) === false) {
            end();
            return;
        }
        ++cnt;
        // if not finished with desired number of iterations,
        // schedule the next iteration
        if (cnt < numTimes) {
            setTimeout(next, delay);
        } else {
            end();
        }
    }
    // start first iteration
    next();
    return d.promise();
}


runIteration(function(i) {
    log(i);
}, 10, 1000).done(function() {
    log("done");
});

Ответ 7

Это решение с простым таймаутом... Возможно, оно не соответствует точному ожидаемому, но попытка сделать "паузу" с javascript не очень подходит в моем совете. Я предлагаю вам искать другой способ делать то, что вы хотите. Fiddle

window.my_condition = true;
window.my_i = 0;

function interate() {
    console.log(window.my_i);
    // ... your code
    if (window.my_condition!==false) {
        window.my_i++;
        setTimeout(interate,300);
    }
}

interate();

Ответ 8

большинство ответов здесь совершенно неверны.

Если вы хотите дождаться окончания каждой итерации - тогда вы не хотите использовать цикл for - просто неправильная стратегия для начала.

вам нужно использовать счетчик и счетчик, иначе он будет работать бесконечно.

вот решение:

var optionLimit = 11;
var optionItem = 1;
function do_something_else() {
    if (optionItem < optionLimit) {
        console.log('doing stuff:' + optionItem)
        optionItem++
        dostuff();
    } else {
        console.log('no more stuff to do already reached:' + optionItem)
    }
}
function dostuff(started) {
    if (started) {
        console.log('started doing something');
    } else {
        console.log('find something else to do');
    }
    setTimeout(function () {
        do_something_else();
    }, 3000);
}
dostuff('started doing something');

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

var thingstodo = [
    thing1 = {
        what: 'clean room',
        time: 8000
    },
    thing2 = {
        what: 'laundry',
        time: 9000
    },
    thing3 = {
        what: 'take out trash',
        time: 6000
    },
    thing4 = {
        what: 'wash dishes',
        time: 10000
    }
]
var optionLimit = 0;
// find how many things to do from things to do list
function get_things_todo(time) {
    console.log('heres stuff i can do');
    console.log('====================');
    for (var i = 0; i < thingstodo.length; i++) {
        val = thingstodo[i];
        console.log(JSON.stringify(val.what));
        optionLimit++
    }
    setTimeout(function () {
        startdostuff(3000)
    }, time);
}
var optionItem = 0;
// find the next thing to do on the list
function get_next_thing(time) {
    setTimeout(function () {
        console.log('================================');
        console.log('let me find the next thing to do');
    }, time);
    setTimeout(function () {
        if (optionItem < optionLimit) {            
            val = thingstodo[optionItem];            
            dostuff(3000, val);
            optionItem++
        } else {
            console.log('=====================================================');
            console.log('no more stuff to do i finished everything on the list')
        }
    }, time*1.5);
}
//do stuff with a 3000ms delay
function dostuff(ftime, val) {
    setTimeout(function () {
        console.log('================================');
        console.log('im gonna ' + JSON.stringify(val.what));
        console.log('will finish in: ' + JSON.stringify(val.time) + ' milliseconds');
        setTimeout(function () {
            console.log('========');
            console.log('all done');
            get_next_thing(3000);
        }, val.time);
    }, ftime);
}
//start doing stuff
function startdostuff(time) {
    console.log('========================');
    console.log('just started doing stuff');
    setTimeout(function () {
        get_next_thing(3000);
    }, time);
}
/// get things to first
get_things_todo(3000);

Ответ 9

Вот решение es6. Мне действительно не нравится обертывать setTimeout в функции, когда вы можете просто использовать переменную с областью, подобную этой:

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

Ответ 10

Мой лучший способ работы - "забыть нормальные циклы" в этом случае и использовать эту комбинацию "setInterval" включает в себя "setTimeOut" s:

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