Estoy trabajando en un programa de música que requiere que varios elementos de JavaScript estén sincronizados con otro. He estado usando setInterval
, que funciona muy bien al principio. Sin embargo, con el tiempo, los elementos se desincronizan gradualmente, lo que es malo en un programa de música.
He leído en línea que setTimeout
es más preciso y, de setTimeout
alguna manera, puedes tener bucles. Sin embargo, no he encontrado una versión genérica que ilustre cómo esto es posible.
Básicamente tengo algunas funciones como estas:
//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);
Funciona muy bien, inicialmente, pero en el transcurso de aproximadamente un minuto, los sonidos se desincronizan notablemente como he leído setInterval
. He leído que setTimeout
puede ser más exacto.
¿Alguien podría mostrarme un ejemplo básico de uso setTimeout
para repetir algo indefinidamente? Alternativamente, si hay una manera de lograr resultados más sincronizados setInterval
o incluso con otra función, hágamelo saber.
fuente
setTimeout
que puede calcular cuánto tiempo fue realmente el retraso para ajustar el tiempo para el próximo tiempo de espera.requestAnimationFrame
? Solo tendría que hacer referencia a la hora en que se encuentra el audio cada vez que serequestAnimationFrame
ejecuta su devolución de llamada.Respuestas:
Puede crear un
setTimeout
bucle usando recursividad:function timeout() { setTimeout(function () { // Do Something Here // Then recall the parent function to // create a recursive loop. timeout(); }, 1000); }
El problema con
setInterval()
ysetTimeout()
es que no hay garantía de que su código se ejecute en el tiempo especificado. Al usarlosetTimeout()
y llamarlo de forma recursiva, se asegura de que todas las operaciones anteriores dentro del tiempo de espera se completen antes de que comience la siguiente iteración del código.fuente
setInterval
?setInterval()
vuelva a ejecutar, sus variables y / o datos podrían desincronizarse muy rápido. Si estoy buscando datos, por ejemplo, y el servidor tarda más de 1 segundo en responder, el uso desetInterval()
mis datos anteriores no habría terminado de procesar antes de que continuara mi siguiente operación. Con este enfoque, no se garantiza que su función se inicie cada segundo. Sin embargo, se garantiza que sus datos anteriores habrán terminado de procesarse antes de que comience el siguiente intervalo.Solo para complementar. Si necesita pasar una variable e iterarla, puede hacerlo así:
function start(counter){ if(counter < 10){ setTimeout(function(){ counter++; console.log(counter); start(counter); }, 1000); } } start(0);
Salida:
1 2 3 ... 9 10
Una línea por segundo.
fuente
Dado que ninguno de los tiempos va a ser muy preciso, una forma de utilizar
setTimeout
para ser un poco más preciso es calcular cuánto tiempo ha sido el retraso desde la última iteración y luego ajustar la siguiente iteración según corresponda. Por ejemplo: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); }
Entonces, la primera vez que esperará (al menos) 1000 ms, cuando se ejecute su código, podría ser un poco tarde, digamos 1046 ms, por lo que restamos 46 ms de nuestro retraso para el siguiente ciclo y el siguiente retraso será solo 954 ms. Esto no evitará que el temporizador se dispare tarde (eso es de esperar), pero le ayudará a evitar que los retrasos se acumulen. (Nota: es posible que desee verificar
thisDelay < 0
qué significa que la demora fue más del doble de la demora objetivo y se perdió un ciclo, depende de usted cómo desea manejar ese caso).Por supuesto, esto probablemente no le ayudará a mantener varios temporizadores sincronizados, en cuyo caso es posible que desee averiguar cómo controlarlos todos con el mismo temporizador.
Entonces, mirando su código, todos sus retrasos son múltiplos de 500, por lo que podría hacer algo como esto:
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); }
fuente
La mejor manera de lidiar con la sincronización del audio es con Web Audio Api, tiene un reloj separado que es preciso independientemente de lo que esté sucediendo en el hilo principal. Hay una gran explicación, ejemplos, etc. de Chris Wilson aquí:
http://www.html5rocks.com/en/tutorials/audio/scheduling/
Eche un vistazo a este sitio para obtener más API de audio web, fue desarrollado para hacer exactamente lo que busca.
fuente
setTimeout
van desde insuficientes hasta horriblemente demasiado complejas. Usar una función nativa parece una idea mucho mejor. Si la API no cumple su propósito, le recomendaría que intente encontrar una biblioteca de programación de terceros confiable.Utilizar
setInterval()
setInterval(function(){ alert("Hello"); }, 3000);
Lo anterior se ejecutará
alert("Hello");
cada 3 segundos.fuente
Según su requisito
tenemos el siguiente ejemplo que puede ayudarlo
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) })
fuente
Utilizo esta forma en la vida laboral: "Olvídese de los bucles comunes" en este caso y use esta combinación de "setInterval" que incluye "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) }
PD: Entiende que el comportamiento real de (setTimeOut): todos comenzarán al mismo tiempo "los tres bla bla bla comenzarán la cuenta regresiva en el mismo momento" así que haz un timeout diferente para organizar la ejecución.
PS 2: el ejemplo de bucle de tiempo, pero para bucles de reacción puede usar eventos, prometer async await ..
fuente
// 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); }
fuente
Como señaló alguien más, la API de Web Audio tiene un mejor temporizador.
Pero en general, si estos eventos ocurren de manera constante, ¿qué tal si los pone a todos en el mismo temporizador? Estoy pensando en cómo funciona un secuenciador por pasos .
Prácticamente, ¿podría verse algo así?
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
fuente
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); }
fuente
Creo que es mejor esperar al final de la función.
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); }
Estas tres funciones son necesarias porque las vars de la segunda función se sobrescribirán con el valor predeterminado cada vez. Cuando el puntero del programa alcanza el setTimeout, ya se calcula un paso. Entonces solo la pantalla necesita un poco de tiempo.
fuente
Use let en lugar de var en el código:
for(let i=1;i<=5;i++){setTimeout(()=>{console.log(i)},1000);}
fuente