所以我正在制作一个音乐程序,需要多个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可以更加一致地准确。
您可以使用递归创建setTimeout
循环:
function timeout() {
setTimeout(function () {
// Do Something Here
// Then recall the parent function to
// create a recursive loop.
timeout();
}, 1000);
}
setInterval()
和setTimeout()
的问题在于无法保证您的代码将在指定时间内运行。通过使用setTimeout()
并递归调用它,您可以确保在下一次代码迭代开始之前,超时内的所有先前操作都已完成。
只是补充。如果你需要传递一个变量并迭代它,你可以这样做:
function start(counter){
if(counter < 10){
setTimeout(function(){
counter++;
console.log(counter);
start(counter);
}, 1000);
}
}
start(0);
输出:
1
2
3
...
9
10
每秒一行。
鉴于时间不是非常准确,使用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);
}
处理音频定时的最佳方法是使用Web Audio Api,它有一个单独的时钟,无论主线程发生什么,都是准确的。克里斯威尔逊在这里有一个很好的解释,例子等:
http://www.html5rocks.com/en/tutorials/audio/scheduling/
浏览一下这个网站以获取更多的Web Audio API,它的开发完全符合您的要求。
我在工作生活中使用这种方式:在这种情况下“忘记常见的循环”并使用“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:定时循环的示例,但对于反应循环,您可以使用事件,承诺异步等待..
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);
}
在代码中使用let而不是var:
for(let i=1;i<=5;i++){setTimeout(()=>{console.log(i)},1000);}
我认为最好在函数结束时超时。
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时,已经计算了一步。然后只是屏幕需要一点时间。