Javascript, setTimeout 루프?


91

그래서 저는 여러 자바 스크립트 요소가 다른 요소와 동기화되어야하는 음악 프로그램을 작업하고 있습니다. 처음에는 정말 잘 작동하는 setInterval을 사용해 왔지만 시간이 지남에 따라 요소가 점차적으로 동기화되지 않아 음악 프로그램과 나쁘게됩니다.

나는 온라인에서 setTimeout이 더 정확하다는 것을 읽었으며 어떻게 든 setTimeout 루프를 가질 수 있지만 이것이 어떻게 가능한지 보여주는 일반 버전을 찾지 못했습니다. 누군가가 setTimeout을 사용하여 무기한으로 반복하는 기본 예제를 보여줄 수 있습니까?

감사합니다. 또는 setInterval 또는 다른 함수를 사용하여 더 많은 동기 결과를 얻을 수있는 방법이 있으면 알려주십시오.

편집하다:

기본적으로 다음과 같은 기능이 있습니다.

//drums
setInterval(function {
//code for the drums playing goes here
},8000);

//chords
setInterval(function {
//code for the chords playing goes here
},1000);

//bass
setInterval(function {
//code for the bass playing goes here
},500);

처음에는 매우 잘 작동하지만 약 1 분 동안 내가 읽은대로 소리가 눈에 띄게 동기화되지 않게됩니다. setInterval과 함께 발생합니다.


2
달성하고자하는 것을 보여주는 코드를 게시 해 주시면 더 나은 답변을 드릴 수 있습니다.
Andy

1
나는 setTimeout이 더 정확하다는 것을 온라인에서 읽었습니다 . 어디서 읽었습니까? 링크를 포함하십시오. setTimeout지연이 실제로 다음 시간 초과 시간을 조정하는 데 얼마나 오래 걸 렸는지 계산할 수 있는 경우라고 가정합니다 .
Matt Burland 2014 년

2
어때 requestAnimationFrame? requestAnimationFrame콜백이 실행될 때마다 오디오가있는 시간을 참조하면 됩니다.
재스퍼

5
두 가지 유형의 타이머 모두 정확하다고 보장 할 수 없습니다. 주어진 밀리 초는 최소 대기 시간이지만 나중에 함수를 호출 할 수 있습니다. 여러 간격을 조정하려는 경우 대신 간격을 제어하는 ​​하나로 통합 해보십시오.
Jonathan Lonowski는

1
음악을 화면의 무언가에 동기화하고 싶다면 DOM을 업데이트 할 때 오디오를 통해 시간 진행 상황을 참조해야합니다. 그렇지 않으면 대부분의 시간이 동기화되지 않습니다.
Jasper

답변:


181

setTimeout재귀를 사용하여 루프를 만들 수 있습니다 .

function timeout() {
    setTimeout(function () {
        // Do Something Here
        // Then recall the parent function to
        // create a recursive loop.
        timeout();
    }, 1000);
}

와 문제 setInterval()setTimeout()코드가 지정된 시간에 실행된다는 보장이 없다는 것이다. setTimeout()이를 재귀 적으로 사용 하고 호출하면 다음 코드 반복이 시작되기 전에 시간 제한 내의 모든 이전 작업이 완료되었는지 확인할 수 있습니다.


1
이 방법과 사용의 차이점은 무엇입니까 setInterval?
Jasper

4
이 접근 방식의 문제는 함수를 완료하는 데 1000ms 이상이 걸리면 모든 것이 배가된다는 것입니다. 이것은 매 1000ms마다 실행된다는 보장이 없습니다. setInterval은입니다.
TJC 2014 년

@TJC : 그 정도는 달성하려는 목표에 따라 다릅니다. 이전 함수가 다음 반복 전에 완료되거나 완료되지 않는 것이 더 중요 할 수 있습니다.
Matt Burland 2014 년

4
@TJC 맞습니다. 그러나 setInterval()재실행 전에 이전 작업이 완료되지 않은 경우 변수 및 / 또는 데이터가 매우 빠르게 동기화되지 않을 수 있습니다. 예를 들어 데이터를 위해 ajaxing하고 서버가 응답하는 데 1 초 이상 걸리는 경우 setInterval()다음 작업이 계속되기 전에 이전 데이터를 사용하여 처리가 완료되지 않았을 것입니다. 이 접근 방식을 사용하면 함수가 매초마다 시작된다는 보장은 없습니다. 그러나 다음 간격이 시작되기 전에 이전 데이터 처리가 완료됩니다.
War10ck 2014 년

1
@ War10ck, 음악 기반 환경을 고려할 때 순서가 중요한 경우 변수 또는 ajax 호출을 설정하는 데 사용되지 않을 것이라고 가정했습니다.
TJC 2014 년

30

보충하기 위해서만. 변수를 전달하고 반복해야하는 경우 다음과 같이 할 수 있습니다.

function start(counter){
  if(counter < 10){
    setTimeout(function(){
      counter++;
      console.log(counter);
      start(counter);
    }, 1000);
  }
}
start(0);

산출:

1
2
3
...
9
10

초당 한 줄.


12

어느 시간도 매우 정확하지 않을 것이라는 점을 감안할 때 setTimeout조금 더 정확하게 사용 하는 한 가지 방법 은 마지막 반복 이후 지연 시간을 계산 한 다음 적절하게 다음 반복을 조정하는 것입니다. 예를 들면 :

var myDelay = 1000;
var thisDelay = 1000;
var start = Date.now();

function startTimer() {    
    setTimeout(function() {
        // your code here...
        // calculate the actual number of ms since last time
        var actual = Date.now() - start;
        // subtract any extra ms from the delay for the next cycle
        thisDelay = myDelay - (actual - myDelay);
        start = Date.now();
        // start the timer again
        startTimer();
    }, thisDelay);
}

따라서 처음으로 1000ms를 기다릴 때 코드가 실행될 때 약간 늦을 수 있습니다 (예 : 1046ms). 따라서 다음주기에 대한 지연에서 46ms를 빼면 다음 지연이 954ms에 불과합니다. 이것은 타이머가 늦게 실행되는 것을 막지는 못하지만 (예상되는) 지연을 막는 데 도움이됩니다. (참고 : thisDelay < 0지연이 목표 지연의 두 배 이상이고주기를 놓쳤다는 의미 를 확인 하고 싶을 수 있습니다.

물론 이것은 여러 타이머를 동기화 상태로 유지하는 데 도움이되지 않을 것입니다.이 경우 동일한 타이머로 모든 타이머를 제어하는 ​​방법을 알아 내고 싶을 수 있습니다.

따라서 코드를 보면 모든 지연이 500의 배수이므로 다음과 같이 할 수 있습니다.

var myDelay = 500;
var thisDelay = 500;
var start = Date.now();
var beatCount = 0;

function startTimer() {    
    setTimeout(function() {
        beatCount++;
        // your code here...
        //code for the bass playing goes here  

        if (count%2 === 0) {
            //code for the chords playing goes here (every 1000 ms)
        }

        if (count%16) {
            //code for the drums playing goes here (every 8000 ms)
        }

        // calculate the actual number of ms since last time
        var actual = Date.now() - start;
        // subtract any extra ms from the delay for the next cycle
        thisDelay = myDelay - (actual - myDelay);
        start = Date.now();
        // start the timer again
        startTimer();
    }, thisDelay);
}

1
+1 깔끔한 접근. 나는 다음 실행의 시간 초과 기간을 상쇄하는 것에 대해 생각하지 않았습니다. JavaScript가 다중 스레드가 아니며 일관된 시간 간격으로 실행된다는 보장이 없다는 점을 고려할 때 얻을 수있는 한 정확한 측정에 가까워 질 것입니다.
War10ck 2014 년

이것은 대단합니다! 나는 이것을 시도하고 그것이 어떻게 진행되는지 알려줄 것입니다. 아마 잠시 걸릴거야 그래서 내 코드는 대규모이다
user3084366

고려 사항 :이 코드가 고정 된 (위와 같이 수정 된 경우에도) 시간 루프에서 트리거되는 것은 실제로 문제를 생각하는 잘못된 방법 일 수 있습니다. 실제로 각 간격 길이를 "다음 시간에 재생 예정인 다음 시간 에서 현재 시간을 시간"으로 설정해야 할 수 있습니다 .
mwardm

방금 적어 놓은 솔루션의 더 나은 버전을 만들었습니다. 나는 당신의 변수 이름이 음악의 언어를 사용하고, 내가 시도조차하지 않는 지연을 만들어 내고 관리하는 것을 좋아합니다.
Miguel Valencia

8

오디오 타이밍을 처리하는 가장 좋은 방법은 Web Audio Api를 사용하는 것입니다. 메인 스레드에서 어떤 일이 발생하는지에 관계없이 정확한 별도의 클럭이 있습니다. 여기에 Chris Wilson의 훌륭한 설명, 예제 등이 있습니다.

http://www.html5rocks.com/en/tutorials/audio/scheduling/

더 많은 웹 오디오 API를 위해이 사이트를 둘러보십시오. 그것은 당신이 추구하는 것을 정확히 수행하도록 개발되었습니다.


나는 더 많은 사람들이 이것을 찬성하기를 정말로 바란다. setTimeout불충분 한 것부터 끔찍하게 지나치게 복잡한 것까지의 범위를 사용한 답변 . 네이티브 함수를 사용하는 것이 훨씬 더 나은 생각처럼 보입니다. API가 귀하의 목적에 부합하지 않는 경우 신뢰할 수있는 타사 스케줄링 라이브러리를 찾는 것이 좋습니다.
threeve


3

귀하의 요구 사항에 따라

무언가를 반복하기 위해 setTimeout을 사용하는 기본적인 예를 보여주세요.

우리는 당신을 도울 수있는 다음의 예가 있습니다.

var itr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var  interval = 1000; //one second
itr.forEach((itr, index) => {

  setTimeout(() => {
    console.log(itr)
  }, index * interval)
})


1

저는 직장 생활에서 이렇게 사용합니다.이 경우에는 "공통 루프를 잊어 버림"을 사용하고 "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)의 실제 동작을 이해합니다. 모두 같은 시간에 시작됩니다. "3 bla bla bla는 같은 순간에 카운트 다운을 시작합니다."실행을 정렬하기 위해 다른 시간 제한을 만듭니다.

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


1

솔루션의 setTimeout 루프 문제

// it will print 5 times 5.
for(var i=0;i<5;i++){
setTimeout(()=> 
console.log(i), 
2000)
}               // 5 5 5 5 5

// improved using let
for(let i=0;i<5;i++){
setTimeout(()=> 
console.log('improved using let: '+i), 
2000)
}

// improved using closure
for(var i=0;i<5;i++){
((x)=>{
setTimeout(()=> 
console.log('improved using closure: '+x), 
2000)
})(i);
} 


1
var와 let간에 다르게 작동하는 이유를 아십니까?
Jay

1
@Jay ... 그들 사이의 차이점은 var는 함수 범위이고 let은 블록 범위라는 것입니다. 더 명확한 설명 당신은을 통해 갈 수 medium.com/@josephcardillo/...
바히드 악 타르

0

function appendTaskOnStack(task, ms, loop) {
    window.nextTaskAfter = (window.nextTaskAfter || 0) + ms;

    if (!loop) {
        setTimeout(function() {
            appendTaskOnStack(task, ms, true);
        }, window.nextTaskAfter);
    } 
    else {
        if (task) 
            task.apply(Array(arguments).slice(3,));
        window.nextTaskAfter = 0;
    }
}

for (var n=0; n < 10; n++) {
    appendTaskOnStack(function(){
        console.log(n)
    }, 100);
}


1
귀하의 솔루션에 대한 설명은 대단히 감사하겠습니다!
ton

0

다른 누군가가 지적했듯이 Web Audio API에는 더 나은 타이머가 있습니다.

그러나 일반적으로 이러한 이벤트가 지속적으로 발생한다면 모든 이벤트를 동일한 타이머에 배치하는 것은 어떻습니까? 스텝 시퀀서 가 어떻게 작동 하는지 생각하고 있습니다.

실제로 이렇게 보일 수 있습니까?

var timer = 0;
var limit = 8000; // 8000 will be the point at which the loop repeats

var drumInterval = 8000;
var chordInterval = 1000;
var bassInterval = 500;

setInterval(function {
    timer += 500;

    if (timer == drumInterval) {
        // Do drum stuff
    }

    if (timer == chordInterval) {
        // Do chord stuff
    }

    if (timer == bassInterval) {
        // Do bass stuff
    }

    // Reset timer once it reaches limit
    if (timer == limit) {
        timer = 0;
    }

}, 500); // Set the timer to the smallest common denominator

-2

함수가 끝날 때 시간 초과하는 것이 더 낫다고 생각합니다.

function main(){
    var something; 
    make=function(walkNr){
         if(walkNr===0){
           // var something for this step      
           // do something
         }
         else if(walkNr===1){
           // var something for that step 
           // do something different
         }

         // ***
         // finally
         else if(walkNr===10){
           return something;
         }
         // show progress if you like
         setTimeout(funkion(){make(walkNr)},15,walkNr++);  
   }
return make(0);
}   

두 번째 함수의 vars는 매번 기본값으로 덮어 쓰기 때문에이 세 가지 함수가 필요합니다. 프로그램 포인터가 setTimeout에 도달하면 한 단계가 이미 계산됩니다. 그러면 화면에 약간의 시간이 필요합니다.


-2

코드에서 var 대신 let을 사용하십시오.

for(let i=1;i<=5;i++){setTimeout(()=>{console.log(i)},1000);}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.