Javascript,setTimeout循环?

问题描述 投票:68回答:9

所以我正在制作一个音乐程序,需要多个javascript元素与另一个同步。我一直在使用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);

它最初工作得非常好,但是在大约一分钟的过程中,声音明显不同步,因为我已经阅读了setInterval,我读过setTimeout可以更加一致地准确。

javascript settimeout setinterval timing
9个回答
149
投票

您可以使用递归创建setTimeout循环:

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

setInterval()setTimeout()的问题在于无法保证您的代码将在指定时间内运行。通过使用setTimeout()并递归调用它,您可以确保在下一次代码迭代开始之前,超时内的所有先前操作都已完成。


23
投票

只是补充。如果你需要传递一个变量并迭代它,你可以这样做:

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

输出:

1
2
3
...
9
10

每秒一行。


11
投票

鉴于时间不是非常准确,使用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);
}

所以它第一次等待(至少)1000毫秒,当你的代码被执行时,可能会有点晚,比如1046毫秒,所以我们从下一个周期的延迟中减去46毫秒,下一个延迟将是只有954毫秒。这不会阻止计时器开始延迟(这是预期的),但可以帮助您阻止延迟起球。 (注意:您可能需要检查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);
}

5
投票

处理音频定时的最佳方法是使用Web Audio Api,它有一个单独的时钟,无论主线程发生什么,都是准确的。克里斯威尔逊在这里有一个很好的解释,例子等:

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

浏览一下这个网站以获取更多的Web Audio API,它的开发完全符合您的要求。


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

PS:理解(setTimeOut)的真实行为:它们都会在同一时间开始“三个bla bla bla将在同一时刻开始倒计时”,所以要做出不同的超时来安排执行。

PS 2:定时循环的示例,但对于反应循环,您可以使用事件,承诺异步等待..


1
投票

使用setInterval()

setInterval(function(){
 alert("Hello"); 
}, 3000);

以上将每3秒执行一次alert("Hello");


0
投票

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
投票

在代码中使用let而不是var:

for(let i=1;i<=5;i++){setTimeout(()=>{console.log(i)},1000);}

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

这三个函数是必需的,因为第二个函数中的变量每次都会被默认值覆盖。当程序指针到达setTimeout时,已经计算了一步。然后只是屏幕需要一点时间。

© www.soinside.com 2019 - 2024. All rights reserved.