web-dev-qa-db-fra.com

JavaScript: pour boucle avec timeout

Je veux que ma boucle for ne soit pas exécutée immédiatement, mais attendez le timeout après chaque itération. Par exemple:

for(var i=0; i<10; i++) {
    console.log(i);
    //wait for 1000
}

J'ai trouvé de nombreuses solutions sur le débordement de pile comme celle-ci:

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

Mais dans toutes les implémentations, la boucle attend initialement 3000 millisecondes puis exécute la boucle for en une seule fois. Existe-t-il un moyen d'appeler chaque itération après avoir attendu 1000 millisecondes.

16
Parag Gangil

Vous pouvez travailler cela avec des mathématiques simples:

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

1000ms: 0
4000 ms: 1
7000 ms: 2
10000 ms: 3
13000ms: 4
...


Suite aux commentaires

Il semble que votre demande soit un peu floue. si vous voulez faire quelque chose après le dernier timeout, vous pouvez définir une limite et comparer l'index actuel:

var limit = 10
for (var i=0;i<=limit;i++) {
   (function(ind) {
       setTimeout(function(){
           console.log(ind);
           if(ind === limit){
               console.log('It was the last one');
           }
       }, 1000 + (3000 * ind));
   })(i);
}

Violon: http://jsfiddle.net/Tn4A7/


Je pense que je sais ce que tu veux ...

et c'est simplement faire

for (var i=0;i<=10;i++) {
   (function(ind) {
       setTimeout(function(){console.log(ind);}, 1000 * ind);
   })(i);
}
31
Karl-André Gagnon

Ne faites pas de fonctions dans les boucles, à la place:

(function fiveSeconds  (n) {

  if (n < 5) setTimeout(function () {  
    fiveSeconds ( n ); // Redo if n < 5 (and pass n)
  }, 1000);
  
  console.log( n++ );

} (0)); // Initialize. n is 0

ce qui précède enregistrera dix chiffres de 0 à 5 à 1 seconde d'intervalle.

Navigateurs modernes (et IE10 +)

(function fiveSeconds (n) {

  console.log( n++ );

  if (n <= 5) setTimeout( fiveSeconds, 1000, n ); // Redo if n <= 5 (and pass n)
  
} (0)); // Initialize. n is 0
15
Roko C. Buljan

pourquoi ne pas utiliser quelque chose comme ça:

var i = 0
var id = window.setInterval(function(){
    if(i >= 10) {
        clearInterval(id);
        return;
    }

    console.log(i);
    i++;
}, 1000)
6
CannibalGorilla

Cela marche:

function initiateTimeOut(i) {
  setTimeout(function() { doStuff(i) }, 30);
}
function doStuff(i) {
    console.log(i);
    i++;
    if (i <= 10) {
        initiateTimeOut(i); 
    }
}

initiateTimeOut(0);

de cette façon, vous n'incrémenterez i que lorsque votre fonction s'exécutera, ce que je crois être ce que vous recherchez.

Exemple dans un violon: http://jsfiddle.net/My7Zg/


Ou, encore plus court (http://jsfiddle.net/My7Zg/1/):

function customLoop(i) {
    console.log(i);
    i++;
    if (i<=10) {setTimeout(function(){customLoop(i);},1000);}
}
customLoop(0);
3
agconti

Vous pouvez aborder votre situation de deux manières.

  1. Vous pouvez planifier immédiatement un tas d'appels setTimeout() à des heures variables afin qu'ils s'exécutent aux heures souhaitées à l'avenir (d'autres réponses illustrent ici comment procéder).

  2. Vous pouvez exécuter la première itération, planifier la prochaine itération et faire exécuter la prochaine itération la suivante jusqu'à ce que vous ayez terminé le nombre d'itérations souhaité. C'est finalement un peu plus évolutif que de définir beaucoup d'appels setTimeout() et vous donne plus de liberté de branchement/logique parce que vous contrôlez ce qui se passe ensuite après chaque itération.

Cette deuxième option utilisant une fonction utilitaire plus générale ressemblerait à ceci:

// utility function to call a callback numTimes, 
// separated by delay milliseconds
function runIteration(fn, numTimes, delay) {
    var cnt = 0;
    function next() {
        // call the callback and stop iterating if it returns false
        if (fn(cnt) === false) return;
        ++cnt;
        // if not finished with desired number of iterations,
        // schedule the next iteration
        if (cnt < numTimes) {
            setTimeout(next, delay);
        }
    }
    // start first iteration
    next();

}

Donc, pour exécuter votre instruction console, vous feriez ceci:

runIteration(function(i) {
    console.log(i);
}, 10, 1000);

Démo de travail: http://jsfiddle.net/jfriend00/HqCZ3/

Cela pourrait également être étendu avec une 2e fonction de rappel qui a été appelée lorsque l'itération était terminée (utile dans certaines circonstances) ou renvoyer une promesse qui est résolue lorsque les itérations sont terminées.

Voici à quoi ressemblerait une version qui renvoie une promesse: http://jsfiddle.net/jfriend00/XtJ69/

// utility function to call a callback numTimes, 
// separated by delay milliseconds
function runIteration(fn, numTimes, delay) {
    var d = $.Deferred();
    var cnt = 0;

    function end() {
        d.resolve();
    }

    function next() {
        // call the callback and stop iterating if
        // it returns false
        if (fn(cnt) === false) {
            end();
            return;
        }
        ++cnt;
        // if not finished with desired number of iterations,
        // schedule the next iteration
        if (cnt < numTimes) {
            setTimeout(next, delay);
        } else {
            end();
        }
    }
    // start first iteration
    next();
    return d.promise();
}


runIteration(function(i) {
    log(i);
}, 10, 1000).done(function() {
    log("done");
});
1
jfriend00
for (var i=0;i<=10;i++) {
   (function(ind) {
       setTimeout(function(){console.log((ind + 1)*1000, ':', ind);}, 1000 * (ind+1) );
   })(i);
}

Production:

1000 : 0
2000 : 1
3000 : 2
4000 : 3
5000 : 4
6000 : 5
7000 : 6
8000 : 7
9000 : 8
10000 : 9
11000 : 10

DEMO DE TRAVAIL

1
PeterKA

Ma meilleure façon de travailler est "d'oublier les boucles normales" dans ce cas et d'utiliser cette combinaison de "setInterval" incluant "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 commenceront tous en même temps "les trois bla bla bla commenceront le compte à rebours au même moment" alors faites un timeout différent pour organiser l'exécution.

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

0
Mohamed Abulnasr

la plupart des réponses ici sont complètement fausses.

Si vous voulez attendre la fin de chaque itération --- alors vous ne voulez pas utiliser une boucle for --- simplement la mauvaise stratégie pour commencer.

vous devez utiliser un compteur et une limite de compteur sinon il bouclera sans fin.

voici la solution:

var optionLimit = 11;
var optionItem = 1;
function do_something_else() {
    if (optionItem < optionLimit) {
        console.log('doing stuff:' + optionItem)
        optionItem++
        dostuff();
    } else {
        console.log('no more stuff to do already reached:' + optionItem)
    }
}
function dostuff(started) {
    if (started) {
        console.log('started doing something');
    } else {
        console.log('find something else to do');
    }
    setTimeout(function () {
        do_something_else();
    }, 3000);
}
dostuff('started doing something');

si vous avez un ensemble d'éléments que vous devez indexer --- alors vous pouvez utiliser une boucle pour compter le nombre d'éléments qui doivent être exécutés comme suit:

var thingstodo = [
    thing1 = {
        what: 'clean room',
        time: 8000
    },
    thing2 = {
        what: 'laundry',
        time: 9000
    },
    thing3 = {
        what: 'take out trash',
        time: 6000
    },
    thing4 = {
        what: 'wash dishes',
        time: 10000
    }
]
var optionLimit = 0;
// find how many things to do from things to do list
function get_things_todo(time) {
    console.log('heres stuff i can do');
    console.log('====================');
    for (var i = 0; i < thingstodo.length; i++) {
        val = thingstodo[i];
        console.log(JSON.stringify(val.what));
        optionLimit++
    }
    setTimeout(function () {
        startdostuff(3000)
    }, time);
}
var optionItem = 0;
// find the next thing to do on the list
function get_next_thing(time) {
    setTimeout(function () {
        console.log('================================');
        console.log('let me find the next thing to do');
    }, time);
    setTimeout(function () {
        if (optionItem < optionLimit) {            
            val = thingstodo[optionItem];            
            dostuff(3000, val);
            optionItem++
        } else {
            console.log('=====================================================');
            console.log('no more stuff to do i finished everything on the list')
        }
    }, time*1.5);
}
//do stuff with a 3000ms delay
function dostuff(ftime, val) {
    setTimeout(function () {
        console.log('================================');
        console.log('im gonna ' + JSON.stringify(val.what));
        console.log('will finish in: ' + JSON.stringify(val.time) + ' milliseconds');
        setTimeout(function () {
            console.log('========');
            console.log('all done');
            get_next_thing(3000);
        }, val.time);
    }, ftime);
}
//start doing stuff
function startdostuff(time) {
    console.log('========================');
    console.log('just started doing stuff');
    setTimeout(function () {
        get_next_thing(3000);
    }, time);
}
/// get things to first
get_things_todo(3000);
0
Michael P.

Voici un es6 Solution. Je n'aime vraiment pas encapsuler le setTimeout dans une fonction, quand vous pouvez simplement utiliser une variable de portée de bloc comme celle-ci:

for (let i=0; i<=10; i++) {
    setTimeout(() => {console.log(i);}, 1000 * i);
}
0
Artur Grigio

C'est une solution avec un simple timeout ... Peut-être qu'elle ne correspond pas exactement à ce que vous attendez, mais essayer de faire une "pause" avec javascript n'est pas une bonne approche dans mes conseils. Je vous suggère de chercher une autre façon de faire ce que vous voulez. Violon

window.my_condition = true;
window.my_i = 0;

function interate() {
    console.log(window.my_i);
    // ... your code
    if (window.my_condition!==false) {
        window.my_i++;
        setTimeout(interate,300);
    }
}

interate();
0
weeger