JavaScript 루프에서 지연을 어떻게 추가합니까?


346

while루프 안에 지연 / 수면을 추가하고 싶습니다 .

나는 이것을 이렇게 시도했다 :

alert('hi');

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

첫 번째 시나리오 만 해당됩니다.를 표시 한 후 alert('hi')3 초 동안 기다렸다가 alert('hello')표시되지만 alert('hello')반복적으로 계속 표시됩니다 .

내가 원하는 것은 after alert('hello')가 3 초 후에 표시 alert('hi')되고 두 번째 시간 동안 3 초 동안 기다려야한다는 것 alert('hello')입니다.

답변:


750

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
    console.log('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() {
    console.log('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


27
이것을 구현하기 위해 재귀를 사용하지 않으면 결국 스택 오버플로가 발생하지 않습니까? 백만 번의 반복을 원한다면 이것을 구현하는 더 좋은 방법은 무엇입니까? 아래의 Abel 솔루션과 같이 setInterval을 설정하고 지우십시오.
Adam

7
@ 아담 : 내 이해는 setTimeout이 비 블로킹이기 때문에 이것은 회귀가 아니라는 것입니다-각 setTimeout 후에 스택 창이 닫히고 실행을 기다리는 setTimeout이 하나뿐입니다 ...
Joe

3
for in루프 와 같은 객체를 반복 할 때 어떻게 작동 합니까?
vsync

1
@vsync 살펴보기Object.keys()
Braden Best

1
@joey 혼란 스럽 setTimeout습니다 setInterval. 콜백이 호출되면 시간 초과가 암시 적으로 삭제됩니다.
cdhowie

72

다음과 같이 해보십시오 :

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

69

ES6를 사용 let하는 경우 다음 을 수행 하는 데 사용할 수 있습니다 .

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

루프가 아닌 각 반복 에 대해 let선언 i하는 것은 무엇입니까? 이런 식으로 전달되는 것은 우리가 원하는 것입니다.setTimeout


1
감사! 이 방법을 내 스스로 생각하지 않았을 것입니다. 실제 블록 범위. 그것을 상상해보십시오.
Sophia Gold


1
@Flame_Phoenix 어떤 메모리 할당 문제가 있습니까?
4castle

1
setTimeout 호출 i*3000은 루프 내에서 인수 의 값을 동 기적으로 계산하여 값으로 전달합니다 setTimeout. 사용법 let은 선택 사항이며 질문 및 답변과 관련이 없습니다.
traktor53

@Flame_Phoenix는이 코드에 문제가 있다고 언급했습니다. 기본적으로 첫 번째 패스에서 타이머를 만든 다음 조건에 따라 루프가 끝날 때까지 루프를 반복해서 반복하십시오 ( i < 10). 여러 타이머가 병렬로 작동하여 메모리 할당을 생성하고 반복 횟수가 많을수록 악화됩니다.
XCanG

63

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

MDN에 대한 참조

ES7은 이제 NodeJS 및 최신 브라우저에서 지원되지만 BabelJS 로 변환 하여 어디서나 실행될 수 있습니다.


그것은 나를 위해 잘 작동합니다. 루프를 끊고 싶을 때 await를 사용할 때 어떻게해야합니까?
Sachin Shah

break;아마도 @sachin ?
조나스 윌름

이 솔루션에 감사드립니다. 기존의 모든 제어 구조를 사용하는 것이 좋으며 계속할 필요는 없습니다.
거스

이것은 여전히 ​​다양한 타이머를 생성하고 순서가 아닌 다른 시간에 해결됩니까?
David Yell

@JonasWilms 'Run snippet'버튼을 완전히 놓친 것 같습니다 : facepalm :
David Yell

24

또 다른 방법은 시간 초과 시간을 곱하는 것이지만 이것은 수면과는 다릅니다 . 루프 후 코드는 즉시 실행되며 콜백 함수 실행 만 지연됩니다.

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

첫 번째 제한이 설정 될 것이다 3000 * 1두 번째로, 3000 * 2등등.


2
start이 메소드를 사용하여 함수 내에서 안정적으로 사용할 수는 없습니다 .
DBS

2
나쁜 습관-불필요한 메모리 할당.
Alexander Trakhimenok

창의성에 찬성하지만 나쁜 습관은 아닙니다. :)
Salivan 2016 년

2
왜 나쁜 습관이고 왜 메모리 할당 문제가 있습니까? 이 답변에도 같은 문제가 있습니까? stackoverflow.com/a/36018502/1337392
Flame_Phoenix

1
@Flame_Phoenix 프로그램은 모든 타이머가 동시에 실행되면서 각 루프마다 하나의 타이머를 유지하기 때문에 좋지 않습니다. 따라서 1000 회의 반복이 있으면 처음에 동시에 1000 개의 타이머가 실행됩니다.
Joakim

16

이 작동합니다

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

이 바이올린을 사용해보십시오 : https://jsfiddle.net/wgdx8zqq/


1
그래도 거의 같은 시간에 모든 시간 초과 호출을 트리거합니다.
Eddie

내가 말한 유일한 것은, 나는 이런 식으로 균열을 일으켰지 $.Deferred만 그것은 작동하게하는 다른 시나리오였습니다.
ArifMustafa

15

나는 당신이 이와 같은 것을 필요로한다고 생각합니다 :

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

참고 : 경고를 사용하면 경고를 닫을 때까지 자바 스크립트 실행이 중단됩니다. 요청한 것보다 더 많은 코드가있을 수 있지만 이것은 재사용 가능한 강력한 솔루션입니다.


15

아마 사용할 것입니다 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);

3
SetTimeout은 settinterval보다 훨씬 좋습니다. 구글 그것 그리고 당신은 알 것이다
Airy

14
나는 그것을 조금 봤는데 아무것도 발견하지 못했다. 왜 setInterval이 나쁜가? 우리에게 링크를 줄 수 있습니까? 또는 예? 감사합니다
Marcs

나는 생각 요점은 그했다 SetInterval()심지어 일부 오류 또는 블록의 경우 '스레드'산란 유지합니다.
Mateen Ulhaq

8

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

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>


4

방금 내 2 센트도 여기에 올릴 줄 알았는데 이 함수는 지연과 함께 반복 루프를 실행합니다. 이 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);
}

4

나는 이것을 Bluebird 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>


2

ES6에서는 다음과 같이 할 수 있습니다.

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

ES5에서는 다음과 같이 할 수 있습니다.

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

그 이유는 블록 범위에 관계없이 변수를 전역 적으로 또는 전체적으로 로컬로 정의하는 키워드 let와 달리 블록 문의 범위 또는 var변수 가 사용되는 표현식으로 제한되는 변수를 선언 할 수 있기 때문입니다 .


1

Daniel Vassallo의 답변의 수정 된 버전. 변수를 추출하여 함수를보다 재사용 가능하게 만듭니다.

먼저 몇 가지 필수 변수를 정의하겠습니다.

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

좋은 점과 전역 변수없이 함수에 데이터를 전달하는 방법
Sundara Prabu

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

1

당신은 그것을 할:

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


경고 대신 콘솔 로그를 더 잘 사용하면 30 분 동안 경고를 닫는 것이 재미 있지 않았습니다.)
Hendry

네. 내가 참조! 그러나 요청은 경고입니다 ... huz
응우 엔 바 DANH - FAIC HN

왜 jQuery를 가져 옵니까?
Elias Soares 19

죄송합니다 ... 불필요합니다. 게시물 내용을 모르겠습니다 ... 먼저.
Nguyen Ba Danh-FAIC HN

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

7
함수 이름은 끔찍합니다. 이것이이 코드를 읽기 어려운 주된 이유입니다.
마크 월터스

0

특정 조건에서 지연되는 지연을 갖는 무한 루프를 만드는 방법은 다음과 같습니다.

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

여기서 핵심은 시간 초과로 해결되는 새로운 약속을 만들고 해결을 기다리는 것입니다.

분명히 당신은 그것에 대한 비동기 / 대기 지원이 필요합니다. 노드 8에서 작동합니다.


0

일반적인 사용을 위해 "정상적인 루프를 잊어 버리고"이 "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)
        }

추신 : (setTimeOut)의 실제 동작을 이해하십시오 : 그들은 모두 동시에 같은 시간에 시작됩니다 "세 개의 bla bla bla는 같은 순간에 카운트 다운을 시작합니다".

PS 2 : 타이밍 루프의 예이지만 반응 루프의 경우 이벤트를 사용할 수 있습니다. 약속 async await ..


0

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


1
코드 스 니펫에 대해 최소한 간단한 설명을 제공하십시오. 최소한 다른 사람들 이 질문 을 처리 할 수 있도록하십시오 .
Hexfire

1
미래 독자들에게 많은 정보를 제공하지
않기

0

내 지식으로는 setTimeout함수가 비동기 적으로 호출됩니다. 할 수있는 일은 전체 루프를 비동기 함수로 감싸고 다음 Promise과 같이 setTimeout이 포함 된 a 를 기다리는 것 입니다.

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

비동기 프로그래밍에 대해 잘 이해하려면 약간의 시간이 걸립니다.


0

이것을 시도하십시오

 var arr = ['A','B','C'];
 (function customLoop (arr, i) {
    setTimeout(function () {
    // Do here what you want to do.......
    console.log(arr[i]);
    if (--i) {                
      customLoop(arr, i); 
    }
  }, 2000);
})(arr, arr.length);

결과

A // after 2s
B // after 2s
C // after 2s

-1

배열을 반복하는 데 사용하는 함수는 다음과 같습니다.

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
}

-1

이 스크립트는 대부분의 경우 작동합니다

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

-1

이 시도...

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

-1

루프가 실행되는 동안 2 초마다 텍스트를 표시하는 간단한 구현.

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

-3

이 시도

//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's you code
   },(i+i+1)*1000);
  }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.