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