Javascript 如何使“setInterval”的行为更加同步,或者如何使用“setTimeout”?

Javascript 如何使“setInterval”的行为更加同步,或者如何使用“setTimeout”?,javascript,settimeout,setinterval,timing,Javascript,Settimeout,Setinterval,Timing,我正在开发一个音乐程序,它需要多个JavaScript元素与另一个同步。我一直在使用setInterval,它最初运行得非常好。然而,随着时间的推移,这些元素逐渐变得不同步,这在音乐节目中是不好的 我在网上读到,setTimeout更准确,你可以通过某种方式进行setTimeout循环。然而,我还没有找到一个通用版本来说明这是如何可能的 基本上我有一些类似的功能: //鼓 设置间隔(函数){ //这里是打鼓的代码 }, 8000); //和弦 设置间隔(函数){ //这里是和弦演奏的代码 },

我正在开发一个音乐程序,它需要多个JavaScript元素与另一个同步。我一直在使用
setInterval
,它最初运行得非常好。然而,随着时间的推移,这些元素逐渐变得不同步,这在音乐节目中是不好的

我在网上读到,
setTimeout
更准确,你可以通过某种方式进行
setTimeout
循环。然而,我还没有找到一个通用版本来说明这是如何可能的

基本上我有一些类似的功能:

//鼓
设置间隔(函数){
//这里是打鼓的代码
}, 8000);
//和弦
设置间隔(函数){
//这里是和弦演奏的代码
}, 1000);
//鲈鱼
设置间隔(函数){
//贝司演奏的代码在这里
}, 500);
起初,它工作得非常好,但在大约一分钟的时间里,声音变得明显不同步,正如我读到的
setInterval
。我已经读到,
setTimeout
可以更加一致地准确


有人能给我举一个使用
setTimeout
无限循环的基本例子吗?或者,如果有一种方法可以通过
setInterval
或其他函数获得更同步的结果,请告诉我。

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

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

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

鉴于这两个时间都不太准确,使用
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);
}

我认为最好在函数结束时超时

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时,已经计算了一个步骤。然后只是屏幕需要一点时间

处理音频计时的最佳方法是使用Web audio Api,它有一个独立的时钟,无论主线程中发生了什么,都是准确的。克里斯·威尔逊(Chris Wilson)在这里有一个很好的解释、例子等:


看看这个网站,了解更多的网络音频API,它的开发正是为了满足您的需求

仅用于补充。如果需要传递一个变量并对其进行迭代,可以这样做:

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

每秒一行。

我在工作中使用这种方式:“忘记常见循环”,在这种情况下,使用“设置间隔”的组合包括“设置超时”:

函数iAsk(lvl){
var i=0;
var intr=setInterval(函数(){//启动循环
i++;//递增它
如果(i>lvl){//检查是否达到结束轮。
清除间隔(intr);
返回;
}
setTimeout(函数(){
$(“.imag”).prop(“src”,pPng);//在50毫秒后执行第一个bla bla bla bla bla bla
},50);
setTimeout(函数(){
//100毫秒后再做一次布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉。
seq[i-1]=(Math.ceil(Math.random()*4)).toString();
$(“#hh”).after('
'+i+':rand='+(Math.ceil(Math.random()*4)).toString()+'>'+seq[i-1]); 美元(“#d”+序号[i-1])。道具(“src”,pGif); var d=document.getElementById('aud'); d、 play(); },100); setTimeout(函数(){ //继续添加bla bla bla bla,直到完成:) 美元(“#d”+序号[i-1])。道具(“src”,pPng); },900); },1000);//循环等待时间必须大于等于900(内部操作的最大超时) }
PS:了解(setTimeOut)的真实行为:它们都将在同一时间开始“三个bla bla bla将在同一时刻开始倒计时”,因此,请设置不同的超时来安排执行


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

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

for(let i=1;i<=5;i++){setTimeout(()=>{console.log(i)},1000);}
for(设i=1;i{console.log(i)},1000);}
设置超时循环问题及解决方案

//它将打印5次5。
对于(var i=0;i
控制台日志(i),
2000)
}               // 5 5 5 5 5
//使用let改进
for(设i=0;i
log('使用let:'+i改进),
2000)
}
//使用闭包进行改进
对于(var i=0;i{
设置超时(()=>
log('使用闭包进行了改进:'+x),
2000)
})(i) );
}
使用

上述操作将执行
警报(“Hello”)每3秒一次。



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

函数appendTaskOnStack(任务、ms、循环){ window.nextTaskAfter=(window.nextTaskAfter | | 0)+ms; 如果(!循环){ setTimeout(函数(){
for(let i=1;i<=5;i++){setTimeout(()=>{console.log(i)},1000);}
setInterval(function(){
 alert("Hello"); 
}, 3000);

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

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