Как добавить задержку в цикл 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 ответа:
The
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, чтобы использовать его везде.
в ES6 (ECMAScript 2015) Вы можете повторять с задержкой с генератор и интервал.
генераторы, новая функция ECMAScript 6, являются функциями, которые могут быть сделал паузу и продолжил. Вызов genFunc не выполняет его. Вместо этого возвращает так называемый объект генератора, который позволяет нам управлять genFunc исполнение. genFunc () первоначально приостанавливается в начале своего тело. Метод genObj.далее () продолжает выполнение 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-это указать количество раз, когда он должен испускать numberss
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); }