493 votes

Comment ajouter un délai dans une boucle JavaScript ?

Je voudrais ajouter un délai/sommeil à l'intérieur d'un fichier while boucle :

J'ai essayé comme ça :

alert('hi');

for(var start = 1; start < 10; start++) {
  setTimeout(function () {
    alert('hello');
  }, 3000);
}

Seul le premier scénario est vrai : après avoir montré alert('hi') il attendra 3 secondes, puis alert('hello') s'affichera mais ensuite alert('hello') sera constamment répétée.

Ce que je voudrais, c'est qu'après alert('hello') s'affiche 3 secondes après alert('hi') puis il doit attendre 3 secondes pour la deuxième fois. alert('hello') et ainsi de suite.

0 votes

For(var i=0 ; i < 5 ; i++){delayLoop(i)} ; function delayLoop(i){setTimeout(function(){console.log('printing with 1sec delay'),(i*1000)}

952voto

Daniel Vassallo Points 142049

El setTimeout() est non bloquante et revient immédiatement. Par conséquent, votre boucle va itérer très rapidement et déclencher les déclencheurs de délai de 3 secondes l'un après l'autre en succession rapide. C'est pourquoi votre première alerte apparaît au bout de 3 secondes, et toutes les autres se succèdent sans délai.

Vous pouvez utiliser quelque chose comme ceci à la place :

var i = 1;                  //  set your counter to 1

function myLoop() {         //  create a loop function
  setTimeout(function() {   //  call a 3s setTimeout when the loop is called
    console.log('hello');   //  your code here
    i++;                    //  increment the counter
    if (i < 10) {           //  if the counter < 10, call the loop function
      myLoop();             //  ..  again which will trigger another 
    }                       //  ..  setTimeout()
  }, 3000)
}

myLoop();                   //  start the loop

Vous pourriez également l'améliorer en utilisant une fonction auto-invoquante, en passant le nombre d'itérations comme argument :

(function myLoop(i) {
  setTimeout(function() {
    console.log('hello'); //  your code here                
    if (--i) myLoop(i);   //  decrement i and call myLoop again if i > 0
  }, 3000)
})(10);                   //  pass the number of iterations as an argument

45 votes

L'utilisation de la récursion pour mettre en œuvre cette méthode ne risque-t-elle pas de provoquer un débordement de pile ? Si vous vouliez faire un million d'itérations, quelle serait la meilleure façon de l'implémenter ? Peut-être setInterval et ensuite l'effacer, comme la solution d'Abel ci-dessous ?

0 votes

J'ai remarqué que cela fonctionne aussi si vous retardez l'exécution des fonctions dans la boucle. J'ai essayé de régler le délai d'exécution d'une fonction dans une boucle for mais elle ne transmettait aucune des variables locales.

0 votes

La récursion n'est pas idéale lorsque vous voulez faire beaucoup de boucles... Je trouve que cela ralentit à quelques milliers d'itérations.

81voto

cji Points 2536

Essayez quelque chose comme ça :

var i = 0, howManyTimes = 10;

function f() {
  console.log("hi");
  i++;
  if (i < howManyTimes) {
    setTimeout(f, 3000);
  }
}

f();

0 votes

Merci, vous avez fait ma journée !

24voto

Felix Kling Points 247451

Une autre méthode consiste à multiplier le délai d'attente, mais notez que cela est pas comme le sommeil . Le code après la boucle sera exécuté immédiatement, seule l'exécution de la fonction de rappel est différée.

for (var start = 1; start < 10; start++)
    setTimeout(function () { alert('hello');  }, 3000 * start);

Le premier délai d'attente sera fixé à 3000 * 1 le second à 3000 * 2 et ainsi de suite.

3 votes

Il est utile de souligner que vous ne pouvez pas utiliser de manière fiable start à l'intérieur de votre fonction en utilisant cette méthode.

2 votes

Mauvaise pratique - allocation inutile de mémoire.

0 votes

Upvote pour la créativité, mais c'est une sacrée mauvaise pratique :)

18voto

BGerrissen Points 9274

Je pense que vous avez besoin de quelque chose comme ça :

var TimedQueue = function(defaultDelay){
    this.queue = [];
    this.index = 0;
    this.defaultDelay = defaultDelay || 3000;
};

TimedQueue.prototype = {
    add: function(fn, delay){
        this.queue.push({
            fn: fn,
            delay: delay
        });
    },
    run: function(index){
        (index || index === 0) && (this.index = index);
        this.next();
    },
    next: function(){
        var self = this
        , i = this.index++
        , at = this.queue[i]
        , next = this.queue[this.index]
        if(!at) return;
        at.fn();
        next && setTimeout(function(){
            self.next();
        }, next.delay||this.defaultDelay);
    },
    reset: function(){
        this.index = 0;
    }
}

Code de test :

var now = +new Date();

var x = new TimedQueue(2000);

x.add(function(){
    console.log('hey');
    console.log(+new Date() - now);
});
x.add(function(){
    console.log('ho');
    console.log(+new Date() - now);
}, 3000);
x.add(function(){
    console.log('bye');
    console.log(+new Date() - now);
});

x.run();

Remarque : l'utilisation des alertes bloque l'exécution du javascript jusqu'à ce que vous fermiez l'alerte. C'est peut-être plus de code que ce que vous avez demandé, mais c'est une solution robuste et réutilisable.

16voto

Abel Terefe Points 331

J'utiliserais probablement setInteval . Comme ça,

var period = 1000; // ms
var endTime = 10000;  // ms
var counter = 0;
var sleepyAlert = setInterval(function(){
    alert('Hello');
    if(counter === endTime){
       clearInterval(sleepyAlert);
    }
    counter += period;
}, period);

3 votes

SetTimeout est bien meilleur que settinterval. Googlez-le et vous le saurez

17 votes

J'ai cherché un peu sur Google et je n'ai rien trouvé, pourquoi setInterval est mauvais ? Pouvez-vous nous donner un lien ? ou un exemple ? Merci

0 votes

Je suppose le point était que SetInterval() continue à créer des "threads" même en cas d'erreur ou de blocage.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X