问题
So I am working on a music program that requires multiple javascript elements to be in sync with another. I've been using setInterval which works really well initially however over time the elements gradually become out of sync which with a music program is bad.
I've read online that setTimeout is more accurate, and you can have setTimeout loops somehow however I have not found a generic version that illustrates how this is possible. Could someone just show me a basic example of using setTimeout to loop something indefinitely.
Thank you. Alternatively, if there is a way to achieve more synchronous results with setInterval or even another function, please let me know.
EDIT:
Basically I have some functions like such:
//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);
It works super well initially but over the course of about a minute, the sounds become noticeably out of sync as I've read happens with setInterval, I've read that setTimeout can be more consistently accurate.
回答1:
You can create a setTimeout
loop using recursion:
function timeout() {
setTimeout(function () {
// Do Something Here
// Then recall the parent function to
// create a recursive loop.
timeout();
}, 1000);
}
The problem with setInterval()
and setTimeout()
is that there is no guarantee your code will run in the specified time. By using setTimeout()
and calling it recursively, you're ensuring that all previous operations inside the timeout are complete before the next iteration of the code begins.
回答2:
Only to supplement. If you need to pass a variable and iterate it, you can do just like so:
function start(counter){
if(counter < 10){
setTimeout(function(){
counter++;
console.log(counter);
start(counter);
}, 1000);
}
}
start(0);
Output:
1
2
3
...
9
10
One line per second.
回答3:
Given that neither time is going to be very accurate, one way to use setTimeout
to be a little more accurate is to calculate how long the delay was since the last iteration, and then adjust the next iteration as appropriate. For example:
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);
}
So the first time it'll wait (at least) 1000 ms, when your code gets executed, it might be a little late, say 1046 ms, so we subtract 46 ms from our delay for the next cycle and the next delay will be only 954 ms. This won't stop the timer from firing late (that's to be expected), but helps you to stop the delays from pilling up. (Note: you might want to check for thisDelay < 0
which means the delay was more than double your target delay and you missed a cycle - up to you how you want to handle that case).
Of course, this probably won't help you keep several timers in sync, in which case you might want to figure out how to control them all with the same timer.
So looking at your code, all your delays are a multiple of 500, so you could do something like this:
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);
}
回答4:
The best way to deal with audio timing is with the Web Audio Api, it has a separate clock that is accurate regardless of what is happening in the main thread. There is a great explanation, examples, etc from Chris Wilson here:
http://www.html5rocks.com/en/tutorials/audio/scheduling/
Have a look around this site for more Web Audio API, it was developed to do exactly what you are after.
回答5:
Use setInterval()
setInterval(function(){
alert("Hello");
}, 3000);
The above will execute alert("Hello");
every 3 seconds.
回答6:
I use this way in work life: "Forget common loops" in this case and use this combination of "setInterval" includes "setTimeOut"s:
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: Understand that the real behavior of (setTimeOut): they all will start in same time "the three bla bla bla will start counting down in the same moment" so make a different timeout to arrange the execution.
PS 2: the example for timing loop, but for a reaction loops you can use events, promise async await ..
回答7:
setTimeout loop problem with solution
// 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);
}
回答8:
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);
}
回答9:
I think it's better to timeout at the end of the function.
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);
}
This three functions are necessary because vars in the second function will be overwritten with default value each time. When the program pointer reach the setTimeout one step is already calculated. Then just the screen needs a little time.
回答10:
Use let instead of var in code :
for(let i=1;i<=5;i++){setTimeout(()=>{console.log(i)},1000);}
来源:https://stackoverflow.com/questions/22154129/javascript-settimeout-loops