admin管理员组文章数量:1134605
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 is bad in a music program.
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.
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.
Could someone just show me a basic example of using setTimeout
to loop something indefinitely? Alternatively, if there is a way to achieve more synchronous results with setInterval
or even another function, please let me know.
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 is bad in a music program.
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.
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.
Could someone just show me a basic example of using setTimeout
to loop something indefinitely? Alternatively, if there is a way to achieve more synchronous results with setInterval
or even another function, please let me know.
13 Answers
Reset to default 199You 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.
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.
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);
}
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.
According to your requirement
just show me a basic example of using setTimeout to loop something
we have following example which can help you
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)
})
Use setInterval()
setInterval(function(){
alert("Hello");
}, 3000);
The above code will execute alert("Hello");
every 3 seconds.
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 ..
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);
}
As someone else pointed out, the Web Audio API has a better timer.
But in general, if these events happen consistently, how about you put them all on the same timer? I'm thinking about how a step sequencer works.
Practically, could it looks something like this?
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
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);
}
function timerCycle() {
if (stoptime == false) {
sec = parseInt(sec);
min = parseInt(min);
hr = parseInt(hr);
sec = sec + 1;
if (sec == 60) {
min = min + 1;
sec = 0;
}
if (min == 60) {
hr = hr + 1;
min = 0;
sec = 0;
}
if (sec < 10 || sec == 0) {
sec = "0" + sec;
}
if (min < 10 || min == 0) {
min = "0" + min;
}
if (hr < 10 || hr == 0) {
hr = "0" + hr;
}
timer.innerHTML = hr + " : " + min + " : " + sec;
setTimeout(timerCycle, 1000);
}
}
function startTimer() {
if (stoptime == true) {
stoptime = false;
timerCycle();
}
}
function stopTimer() {
if (stoptime == false) {
stoptime = true;
}
}
function resetTimer() {
hr = 0;
min = 0;
sec = 0;
stopTimer();
timer.innerHTML = "00:00:00";
}
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.
Use let instead of var in code :
for(let i=1;i<=5;i++){setTimeout(()=>{console.log(i)},1000);}
本文标签:
版权声明:本文标题:javascript - How to make `setInterval` behave more in sync, or how to use `setTimeout` instead? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736784616a1952793.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
setTimeout
you can calculate how long the delay really was a adjust the time for the next timeout. – Matt Burland Commented Mar 3, 2014 at 18:14requestAnimationFrame
? You'd just have to reference the time that the audio is at each time yourrequestAnimationFrame
callback runs. – Jasper Commented Mar 3, 2014 at 18:16