75 votes

Pourquoi setTimeout n'annule-t-il pas ma boucle ?

Je me suis demandé combien de fois un JavaScript while (dans la console de Chrome) peut incrémenter une variable en une milliseconde, j'ai donc rapidement écrit cet extrait directement dans la console :

var run = true, i = 0;
setTimeout(function(){ run = false; }, 1);
while(run){ i++; }

Le problème, c'est qu'il tourne éternellement.
Pourquoi cela se produit-il, et comment puis-je résoudre ce problème ?

80voto

Sirko Points 32515

On en revient à la nature monofilaire de JavaScript. 1 . Ce qui se passe, c'est à peu près ça :

  1. Vos variables sont assignées.
  2. Vous programmez une fonction, pour définir run = false . Il est prévu de l'exécuter après la fonction courante est exécutée (ou toute autre fonction actuellement active).
  3. Vous avez votre boucle sans fin et restez à l'intérieur de la fonction courante.
  4. Après votre boucle sans fin ( jamais ), le setTimeout() sera exécuté et run=false .

Comme vous pouvez le voir, un setTimeout() ne fonctionnera pas ici. Vous pouvez contourner ce problème en vérifiant l'heure dans le fichier while mais cela altérera votre mesure réelle.

1 Au moins, à des fins plus pratiques, vous pouvez le voir comme étant monofilaire. En fait, il existe une boucle dite "d'événements". Dans cette boucle, toutes les fonctions sont mises en file d'attente jusqu'à ce qu'elles soient exécutées. Si vous mettez une nouvelle fonction en file d'attente, elle est placée à la position correspondante dans cette file. Après la fonction courante est terminée, le moteur prend la fonction suivante dans la file d'attente (en respectant les délais tels qu'introduits, par exemple, par setTimeout() et l'exécute.
Par conséquent, à chaque instant, une seule fonction est exécutée, ce qui rend l'exécution pratiquement monofilaire. Il y a quelques exceptions pour les événements, qui sont discutées dans le lien ci-dessous.


Pour référence :

24voto

this.lau_ Points 23290

JavaScript est monofilaire. Ainsi, lorsque vous êtes dans la boucle, rien d'autre n'est exécuté.

19voto

SyntaxLAMP Points 671

Pour conserver la vitesse réelle de Chrome sans devoir constamment récupérer le temps pour calculer la vitesse, vous pouvez essayer ce code JS :

var start = new Date().getTime()
var i = 0
while(i<1000000)
{
    i++
}
var end = new Date().getTime()
var delta = end-start
var speed = i/delta
console.log(speed + " loops per millisecond")

4voto

Guilro Points 1728

Javascript est monofilaire, c'est-à-dire qu'il n'exécute qu'une seule instruction à la fois, de manière séquentielle.

Le système d'événements, comme dans de nombreux autres langages et bibliothèques, est géré par une boucle d'événements. La boucle d'événement est essentiellement une boucle qui, à chaque itération, vérifie les messages dans la file d'attente et distribue les événements.

En javascript (comme dans la plupart des langages mettant en œuvre ce modèle), la boucle d'événement est appelée lorsque la pile est vide, c'est-à-dire lorsque toutes les fonctions ont un retour, en d'autres termes, à la fin du code du programme.

Votre "vrai" programme ressemble à quelque chose comme ça derrière la scène :

var run = true, i = 0;
setTimeout(function(){ run = false; }, 1);
while(run){ i++; }

while(true) {
/*
 * check for new messages in the queue and dispatch
 * events if there are some
 */
  processEvents();
}

Ainsi, le message de l'horloge indiquant que le délai d'attente est terminé n'est jamais traité.

Plus d'informations sur la boucle de l'événement sur : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop


Bien sûr, c'est un peu plus complexe, vérifiez ici ces exemples : Le javascript est-il garanti comme étant monofilaire ? ( tl;dr : Dans certains moteurs de navigateur, certains événements externes ne dépendent pas de la boucle d'événements et sont immédiatement déclenchés lorsqu'ils se produisent, préemptant la tâche en cours. Mais ce n'est pas le cas de setTimeout, qui se contente d'ajouter un message à la file d'attente et ne se déclenche jamais immédiatement).

2voto

ChriskOlson Points 327

La boucle While n'a pas accès au setTimeout. Vous avez du code qui fixe l'exécution à true, et ensuite il ne deviendra jamais false.

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