Estou trabalhando em um programa de música que requer vários elementos JavaScript para estar em sincronia com outro. Tenho usado setInterval
, o que funciona muito bem inicialmente. No entanto, com o tempo, os elementos ficam gradualmente fora de sincronia, o que é ruim em um programa de música.
Eu li online que setTimeout
é mais preciso e você pode ter setTimeout
loops de alguma forma. No entanto, não encontrei uma versão genérica que ilustre como isso é possível.
Basicamente, tenho algumas funções como:
//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 muito bem, inicialmente, mas ao longo de cerca de um minuto, os sons tornam-se visivelmente fora de sincronia conforme li acontece com setInterval
. Eu li que setTimeout
pode ser mais preciso.
Alguém poderia me mostrar um exemplo básico de uso setTimeout
para fazer um loop indefinido de algo? Como alternativa, se houver uma maneira de obter resultados mais sincronizados com setInterval
ou mesmo outra função, entre em contato.
fonte
setTimeout
você pode calcular quanto tempo o atraso realmente foi para ajustar o tempo para o próximo tempo limite.requestAnimationFrame
? Você só precisa fazer referência ao tempo em que o áudio está a cada vez que seurequestAnimationFrame
retorno de chamada é executado.Respostas:
Você pode criar um
setTimeout
loop usando recursão:function timeout() { setTimeout(function () { // Do Something Here // Then recall the parent function to // create a recursive loop. timeout(); }, 1000); }
O problema com
setInterval()
esetTimeout()
é que não há garantia de que seu código será executado no tempo especificado. Ao usásetTimeout()
-lo e chamá-lo recursivamente, você está garantindo que todas as operações anteriores dentro do tempo limite sejam concluídas antes que a próxima iteração do código comece.fonte
setInterval
?setInterval()
de serem executadas novamente, suas variáveis e / ou dados podem ficar fora de sincronia muito rápido. Se eu estiver buscando dados, por exemplo, e o servidor demorar mais de 1 segundo para responder, usarsetInterval()
meus dados anteriores não teria concluído o processamento antes de minha próxima operação continuar. Com essa abordagem, não há garantia de que sua função será iniciada a cada segundo. No entanto, é garantido que seus dados anteriores terão concluído o processamento antes do início do próximo intervalo.Apenas para complementar. Se você precisar passar uma variável e iterá-la, pode fazer da seguinte forma:
function start(counter){ if(counter < 10){ setTimeout(function(){ counter++; console.log(counter); start(counter); }, 1000); } } start(0);
Resultado:
1 2 3 ... 9 10
Uma linha por segundo.
fonte
Dado que nenhum dos tempos será muito preciso, uma maneira de usar
setTimeout
para ser um pouco mais preciso é calcular quanto tempo o atraso ocorreu desde a última iteração e, em seguida, ajustar a próxima iteração conforme apropriado. Por exemplo: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); }
Então, na primeira vez que ele vai esperar (pelo menos) 1000 ms, quando seu código for executado, pode ser um pouco tarde, digamos 1046 ms, então subtraímos 46 ms do nosso atraso para o próximo ciclo e o próximo atraso será apenas 954 ms. Isso não impedirá o cronômetro de disparar atrasado (isso é esperado), mas ajuda a impedir que os atrasos se acumulem. (Nota: você pode querer verificar
thisDelay < 0
que significa que o atraso foi mais do que o dobro do seu atraso alvo e você perdeu um ciclo - depende de como você deseja lidar com esse caso).Claro, isso provavelmente não ajudará você a manter vários cronômetros em sincronia, caso em que você pode querer descobrir como controlá-los todos com o mesmo cronômetro.
Então, olhando para seu código, todos os seus atrasos são múltiplos de 500, então você poderia fazer algo assim:
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); }
fonte
A melhor maneira de lidar com o tempo de áudio é com a API de áudio da Web, ela tem um relógio separado que é preciso, independentemente do que está acontecendo no thread principal. Há uma ótima explicação, exemplos, etc. de Chris Wilson aqui:
http://www.html5rocks.com/en/tutorials/audio/scheduling/
Dê uma olhada neste site para mais API de áudio da web, ela foi desenvolvida para fazer exatamente o que você procura.
fonte
setTimeout
variam de insuficiente a terrivelmente complexo. Usar uma função nativa parece uma ideia muito melhor. Se a API não servir ao seu propósito, eu recomendo tentar encontrar uma biblioteca de programação terceirizada confiável.Usar
setInterval()
setInterval(function(){ alert("Hello"); }, 3000);
O comando acima será executado a
alert("Hello");
cada 3 segundos.fonte
De acordo com sua exigência
temos o seguinte exemplo que pode ajudá-lo
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) })
fonte
Eu uso esta forma na vida profissional: "Esqueça os loops comuns" neste caso e uso esta combinação de "setInterval" inclui "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: Entenda que o comportamento real de (setTimeOut): todos eles começarão no mesmo tempo "os três blá blá blá começarão a contagem regressiva no mesmo momento", então faça um timeout diferente para organizar a execução.
PS 2: o exemplo para loop de tempo, mas para loops de reação você pode usar eventos, promessa assíncrona, espera ..
fonte
// 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); }
fonte
Como outra pessoa observou, a API de áudio da web tem um cronômetro melhor.
Mas, em geral, se esses eventos acontecerem de forma consistente, que tal colocá-los todos no mesmo cronômetro? Estou pensando em como um sequenciador de etapas .
Praticamente, poderia ser algo assim?
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
fonte
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); }
fonte
Eu acho que é melhor fazer o tempo limite no final da função.
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); }
Essas três funções são necessárias porque os vars na segunda função serão sobrescritos com o valor padrão a cada vez. Quando o ponteiro do programa atinge setTimeout, um passo já está calculado. Então, só a tela precisa de um pouco de tempo.
fonte
Use let em vez de var no código:
for(let i=1;i<=5;i++){setTimeout(()=>{console.log(i)},1000);}
fonte