web-dev-qa-db-fra.com

Comment ajouter un délai dans une boucle JavaScript?

J'aimerais ajouter un délai/sommeil dans une boucle while:

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 affiché alert('hi'), il attendra 3 secondes, puis alert('hello') sera affiché, puis alert('hello') sera à plusieurs reprises constamment. 

Ce que j'aimerais, c'est qu'après que alert('hello') soit affiché 3 secondes après alert('hi'), il doit attendre 3 secondes pour la deuxième fois alert('hello') et ainsi de suite.

274
olidev

La fonction setTimeout() est non bloquante et reviendra immédiatement. Par conséquent, votre boucle itérera très rapidement et déclenchera rapidement des déclenchements de temporisation de 3 secondes les uns après les autres. C'est pourquoi vos premières alertes apparaissent au bout de 3 secondes et tout le reste s'enchaîne sans délai.

Vous voudrez peut-être 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
      alert('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 pouvez également le nettoyer en utilisant une fonction d'invocation automatique, en passant le nombre d'itérations sous forme d'argument:

(function myLoop (i) {          
   setTimeout(function () {   
      alert('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
642
Daniel Vassallo

Essayez quelque chose comme ça:

var i = 0, howManyTimes = 10;
function f() {
    alert( "hi" );
    i++;
    if( i < howManyTimes ){
        setTimeout( f, 3000 );
    }
}
f();
57
cji

Si vous utilisez ES6, vous pouvez utiliser let pour y parvenir:

for (let i=1; i<10; i++) {
    setTimeout( function timer(){
        alert("hello world");
    }, i*3000 );
}

Ce que let fait est de déclarer i pour chaque itération , pas la boucle. De cette façon, ce qui est passé à setTimeout correspond exactement à ce que nous voulons.

44
Saket Mehta

Une autre méthode consiste à multiplier le délai d’expiration, mais notez que c’est pas comme dormir . Code après l'exécution de la boucle 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 sera défini sur 3000 * 1, le second sur 3000 * 2 et ainsi de suite.

21
Felix Kling

Depuis ES7, il existe un meilleur moyen de wait une boucle:

function timer(ms) {
 return new Promise(res => setTimeout(res, ms));
}

async function load () {
  for (var i = 0; i < 3; i++) {
    console.log(i);
    await timer(3000);
  }
}

load();

Référence sur MDN

Notez qu'ES7 est rarement pris en charge aujourd'hui, vous devez donc utiliser Babel pour pouvoir l'utiliser partout.

Transpilé

18
Jonas Wilms

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 code javascript jusqu'à la fermeture de l'alerte ... Il peut s'agir de plus de code que ce que vous avez demandé, mais il s'agit d'une solution réutilisable robuste.

14
BGerrissen

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);
13
Abel Terefe

Ça va marcher

for (var i = 0; i < 10; i++) {
    (function(i) {
        setTimeout(function() { console.log(i); }, 100 * i);
    })(i);
}

Essayez ce violon: https://jsfiddle.net/wgdx8zqq/

12
Gsvp Nagaraju

Dans ES6 (ECMAScript 2015), vous pouvez effectuer une itération avec délai avec generator et interval.

Les générateurs, une nouvelle fonctionnalité de ECMAScript 6, sont des fonctions qui peuvent être. suspendu et repris. L'appel de genFunc ne l'exécute pas. Au lieu de cela, il retourne un objet générateur qui nous permet de contrôler .f. de genFunc. exécution. genFunc () est initialement suspendu au début de son corps. La méthode genObj.next () continue l'exécution de genFunc, jusqu'au prochain rendement . (Exploring ES6)


Exemple de code:

let arr = [1, 2, 3, 'b'];
let genObj = genFunc();

let val = genObj.next();
console.log(val.value);

let interval = setInterval(() => {
  val = genObj.next();
  
  if (val.done) {
    clearInterval(interval);
  } else {
    console.log(val.value);
  }
}, 1000);

function* genFunc() {
  for(let item of arr) {
    yield item;
  }
}

Donc, si vous utilisez ES6, c’est le moyen le plus élégant de réaliser une boucle avec délai (à mon avis).

7
Itay Radotzki

Je le fais avec le Promise.delay et la récursion de Bluebird.

function myLoop(i) {
  return Promise.delay(1000)
    .then(function() {
      if (i > 0) {
        alert('hello');
        return myLoop(i -= 1);
      }
    });
}

myLoop(3);
<script src="//cdnjs.cloudflare.com/ajax/libs/bluebird/2.9.4/bluebird.min.js"></script>

4
Dave Bryand

Je pensais juste poster mes deux cents ici aussi. Cette fonction exécute une boucle itérative avec un délai. Voir ce jsfiddle . La fonction est la suivante:

function timeout(range, time, callback){
    var i = range[0];                
    callback(i);
    Loop();
    function Loop(){
        setTimeout(function(){
            i++;
            if (i<range[1]){
                callback(i);
                Loop();
            }
        }, time*1000)
    } 
}

Par exemple:

//This function prints the loop number every second
timeout([0, 5], 1, function(i){
    console.log(i);
});

Serait équivalent à:

//This function prints the loop number instantly
for (var i = 0; i<5; i++){
    console.log(i);
}
3
D Slee

Vous pouvez utiliser l'opérateur RxJS intervalle . Intervalle émet un entier toutes les x secondes et prend le nombre de fois qu'il doit émettre des nombres

Rx.Observable
  .interval(1000)
  .take(10)
  .subscribe((x) => console.log(x))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.lite.min.js"></script>

2
Vlad Bezden

    var startIndex = 0;
    var data = [1, 2, 3];
    var timeout = 1000;

    function functionToRun(i, length) {
      alert(data[i]);
    }

    (function forWithDelay(i, length, fn, delay) {
      setTimeout(function() {
        fn(i, length);
        i++;
        if (i < length) {
          forWithDelay(i, length, fn, delay);
        }
      }, delay);
    })(startIndex, data.length, functionToRun, timeout);

Une version modifiée de la réponse de Daniel Vassallo, avec des variables extraites dans des paramètres pour rendre la fonction plus réutilisable:

Commençons par définir quelques variables essentielles:

var startIndex = 0;
var data = [1, 2, 3];
var timeout = 3000;

Ensuite, vous devez définir la fonction que vous souhaitez exécuter. Cela passera i, l'index actuel de la boucle et la longueur de la boucle, au cas où vous en auriez besoin:

function functionToRun(i, length) {
    alert(data[i]);
}

Version à exécution automatique

(function forWithDelay(i, length, fn, delay) {
   setTimeout(function () {
      fn(i, length);
      i++;
      if (i < length) {
         forWithDelay(i, length, fn, delay); 
      }
  }, delay);
})(startIndex, data.length, functionToRun, timeout);

Version fonctionnelle

function forWithDelay(i, length, fn, delay) {
   setTimeout(function () {
      fn(i, length);
      i++;
      if (i < length) {
         forWithDelay(i, length, fn, delay); 
      }
  }, delay);
}

forWithDelay(startIndex, data.length, functionToRun, timeout); // Lets run it
1
Jasdeep Khalsa

Tu le fais:

alert('hi')
let start = 1
setTimeout(function(){
  let interval = setInterval(function(){
    if(start == 10) clearInterval(interval)
    start++
    alert('hello')
  }, 3000)
}, 3000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
0

Mise en œuvre simple: affichage d'un morceau de texte toutes les deux secondes tant que la boucle est en cours d'exécution.

for (var i = 0; i < foo.length; i++) {
   setInterval(function(){ 
     console.log("I will appear every 2 seconds"); 
   }, 2000);
  break;
};
0
squeekyDave

Voici comment j'ai créé une boucle infinie avec un délai qui se rompt à certaines conditions:

  // Now continuously check the app status until it's completed, 
  // failed or times out. The isFinished() will throw exception if
  // there is a failure.
  while (true) {
    let status = await this.api.getStatus(appId);
    if (isFinished(status)) {
      break;
    } else {
      // Delay before running the next loop iteration:
      await new Promise(resolve => setTimeout(resolve, 3000));
    }
  }

La clé ici est de créer une nouvelle promesse résolue par délai d'attente et d'attendre qu'elle soit résolue. 

De toute évidence, vous avez besoin d’un soutien asynchrone/wait pour cela. Fonctionne dans le nœud 8.

0
K48

<!DOCTYPE html>
<html>
<body>

<button onclick="myFunction()">Try it</button>

<p id="demo"></p>

<script>
function myFunction() {
    for(var i=0; i<5; i++) {
    	var sno = i+1;
       	(function myLoop (i) {          
             setTimeout(function () {   
             	alert(i); // Do your function here 
             }, 1000*i);
        })(sno);
    }
}
</script>

</body>
</html>

0
Boginaathan M

pour une utilisation commune "oublier les boucles normales" et utiliser cette combinaison de "setInterval" inclut "setTimeOut": comme ceci (de mes tâches réelles).

        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" alors faites un temps 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.

0
Mohamed Abulnasr

À ma connaissance, la fonction setTimeout est appelée de manière asynchrone. Ce que vous pouvez faire est d’envelopper la boucle entière dans une fonction asynchrone et d’attendre une Promise contenant le setTimeout comme indiqué:

var looper = async function () {
  for (var start = 1; start < 10; start++) {
    await new Promise(function (resolve, reject) {
      setTimeout(function () {
        console.log("iteration: " + start.toString());
        resolve(true);
      }, 1000);
    });
  }
  return true;
}

Et puis vous appelez le lancer comme ça:

looper().then(function(){
  console.log("DONE!")
});

Veuillez prendre le temps de bien comprendre la programmation asynchrone.

0
Questionare232
/* 
  Use Recursive  and setTimeout 
  call below function will run loop loopFunctionNeedCheck until 
  conditionCheckAfterRunFn = true, if conditionCheckAfterRunFn == false : delay 
  reRunAfterMs miliseconds and continue loop
  tested code, thanks
*/

function functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn,
 loopFunctionNeedCheck) {
    loopFunctionNeedCheck();
    var result = conditionCheckAfterRunFn();
    //check after run
    if (!result) {
        setTimeout(function () {
            functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn, loopFunctionNeedCheck)
        }, reRunAfterMs);
    }
    else  console.log("completed, thanks");    
            //if you need call a function after completed add code call callback in here
}

//passing-parameters-to-a-callback-function
// From Prototype.js 
if (!Function.prototype.bind) { // check if native implementation available
    Function.prototype.bind = function () {
        var fn = this, args = Array.prototype.slice.call(arguments),
            object = args.shift();
        return function () {
            return fn.apply(object,
              args.concat(Array.prototype.slice.call(arguments)));
        };
    };
}

//test code: 
var result = 0; 
console.log("---> init result is " + result);
var functionNeedRun = function (step) {           
   result+=step;    
       console.log("current result is " + result);  
}
var checkResultFunction = function () {
    return result==100;
}  

//call this function will run loop functionNeedRun and delay 500 miliseconds until result=100    
functionRepeatUntilConditionTrue(500, checkResultFunction , functionNeedRun.bind(null, 5));

//result log from console:
/*
---> init result is 0
current result is 5
undefined
current result is 10
current result is 15
current result is 20
current result is 25
current result is 30
current result is 35
current result is 40
current result is 45
current result is 50
current result is 55
current result is 60
current result is 65
current result is 70
current result is 75
current result is 80
current result is 85
current result is 90
current result is 95
current result is 100
completed, thanks
*/
0
user2913925