web-dev-qa-db-fra.com

Javascript, boucles setTimeout?

Je travaille donc sur un programme musical qui nécessite la synchronisation de plusieurs éléments javascript. J'utilise setInterval, qui fonctionne très bien au départ, mais au fil du temps, les éléments deviennent progressivement désynchronisés, ce qui est mauvais avec un programme musical.

J'ai lu en ligne que setTimeout est plus précis et que vous pouvez avoir des boucles setTimeout, mais je n'ai pas trouvé de version générique illustrant comment cela est possible. Quelqu'un pourrait-il me montrer un exemple élémentaire d'utilisation de setTimeout pour mettre en boucle quelque chose indéfiniment?.

Je vous remercie. Autrement, s'il existe un moyen d'obtenir des résultats plus synchrones avec setInterval ou même une autre fonction, veuillez me le faire savoir.

MODIFIER:

En gros, j'ai quelques fonctions comme celles-ci:

//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);

Cela fonctionne très bien au début, mais en l'espace d'une minute environ, les sons deviennent nettement désynchronisés, comme je l'ai lu avec setInterval. J'ai lu que setTimeout peut être toujours plus précis.

74
user3084366

Vous pouvez créer une boucle setTimeout à l'aide de la récursivité:

function timeout() {
    setTimeout(function () {
        // Do Something Here
        // Then recall the parent function to
        // create a recursive loop.
        timeout();
    }, 1000);
}

Le problème avec setInterval() et setTimeout() est qu’il n’ya aucune garantie que votre code sera exécuté dans le délai spécifié. En utilisant setTimeout() et en l'appelant de manière récursive, vous vous assurez que toutes les opérations précédentes dans le délai d'attente sont terminées avant le début de la prochaine itération du code.

155
War10ck

Seulement pour compléter. Si vous avez besoin de passer une variable et de l'itérer, vous pouvez faire comme suit:

function start(counter){
  if(counter < 10){
    setTimeout(function(){
      counter++;
      console.log(counter);
      start(counter);
    }, 1000);
  }
}
start(0);

Sortie:

1
2
3
...
9
10

Une ligne par seconde.

26
João Paulo

Étant donné que ni le temps ne sera très précis, une façon d'utiliser setTimeout pour être un peu plus précis consiste à calculer la durée du délai écoulé depuis la dernière itération, puis à ajuster la prochaine itération en conséquence. Par exemple:

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);
}

Donc la première fois que ça attend (au moins) 1000 ms, quand votre code sera exécuté, il sera peut-être un peu en retard, disons 1046 ms, donc nous soustrayons 46 ms de notre délai du cycle suivant et le prochain délai sera seulement 954 ms. Cela n'empêchera pas le chronomètre de se déclencher tardivement (c'est à prévoir), mais vous aidera à empêcher les retards de se résorber. (Remarque: vous souhaiterez peut-être vérifier thisDelay < 0, ce qui signifie que le délai était plus du double de votre délai cible et que vous avez manqué un cycle - à vous de voir comment vous souhaitez gérer ce cas).

Bien sûr, cela ne vous aidera probablement pas à synchroniser plusieurs minuteries, auquel cas vous voudrez peut-être trouver la meilleure façon de toutes les contrôler avec le même minuteur.

Donc, en regardant votre code, tous vos retards sont un multiple de 500, vous pouvez donc faire quelque chose comme ceci:

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);
}
11
Matt Burland

Le meilleur moyen de gérer le minutage audio est avec l'API Web Audio, qui possède une horloge séparée qui est précise indépendamment de ce qui se passe dans le thread principal. Il y a une excellente explication, des exemples, etc. de Chris Wilson ici:

http://www.html5rocks.com/en/tutorials/audio/scheduling/

Jetez un coup d’œil sur ce site pour plus d’API Web Audio, elle a été développée pour faire exactement ce que vous voulez.

5
Bing

J'utilise cette manière dans la vie professionnelle: "Oubliez les boucles communes" dans ce cas et utilisez cette combinaison de "setInterval" inclut "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: Comprenez que le comportement réel de (setTimeOut): ils vont tous commencer dans le même temps "les trois bla bla bla commenceront à décompter au même moment", faites donc un délai différent pour organiser l’exécution.

PS 2: l'exemple pour la boucle de synchronisation, mais pour une boucle de réaction, vous pouvez utiliser des événements, promettez en attente asynchrone.

1
Mohamed Abulnasr

setTimeout problème de boucle avec 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);
} 
1
Vahid Akhtar

Utilisez setInterval()

setInterval(function(){
 alert("Hello"); 
}, 3000);

Ce qui précède exécutera alert("Hello"); toutes les 3 secondes.

1
Pedro Lobito