Infinite Timer Loop avec javascript (pas de setInterval)?

On m'a demandé (par un ami) de construire une minuterie (infinie qui écrit une ligne à chaque seconde), mais sans setInterval .

Je l'ai résolu avec:

 var i = 0; function k(myId, cb) { setTimeout(function () { console.log(myId); cb(); }, 1000); } function go() { i++; k(i, go); } go(); 

Et ça marche.

Le problème, c'est que je crains qu'il y ait une pression de mémoire. Il crée effectivement une récurrence et après un certain temps (semaine ou quelque chose) – le processus consommera beaucoup de mémoire. (La pile n'est jamais désaffectée)

Comment puis-je changer mon code pour ne pas consommer beaucoup de mémoire?

Ce n'est pas une récurrence

Il peut sembler une récurrence, mais setTimeout ne crée pas de récurrence.

La façon dont setTimeout fonctionne, c'est qu'il revient immédiatement. Donc, l'appel à k se termine immédiatement avec sa pile désallouée.

Lorsque le délai d'attente arrive effectivement et que l'appel à nouveau se reproduit, il ne s'agit pas du point de l'appel précédent vers k mais de la portée globale *.

* Remarque: Je n'utilise pas le sens strict de la portée tel que défini dans la spécification ECMAScript ici. Ce que je veux dire, c'est que l'appel à k sera effectué comme si vous l'avez écrit dans une <script></script> simple, c'est-à-dire hors de toute autre fonction appel.

En ce qui concerne votre préoccupation concernant la fermeture

Dans votre cas particulier, il y a très peu de choses qui sont effectivement enfermées dans la fermeture créée par la fonction k . La seule fermeture significative est la référence aux arguments cb et myId . Et même alors, cela ne dure qu'une seconde environ:

  #1 function k(myId, cb) { #2 setTimeout(function(){ #3 console.log(myId); // there is a closure here to myId #4 cb(); // and another one for cb #5 /* But at this point in the function, setTimeout ends * and as the function returns, there are no remaining * references to either "cb" or "myId" accessible * anywhere else. Which means that the GC can immediately * free them (though in reality the GC may run a bit later) */ #6 }, 1000); // So one second is roughly the longest the closure lasts } 

Peut être plus simple

Je devrais noter que votre code est assez compliqué. Il peut être écrit plus simple et sans utiliser de fermetures (moins la variable globale i) si vous l'écrivez simplement comme ceci:

 // Simpler, does exactly the same thing: var i = 0; function go () { console.log(i); i++; setTimeout(go, 1000); // callback } go(); 

Cette ligne est fausse:

Il crée effectivement une récurrence et après un certain temps (semaine ou quelque chose) – le processus consommera beaucoup de mémoire. (La pile n'est jamais désaffectée)

Il ne crée pas de récurrence, car la fonction sort complètement et est alors appelée à nouveau.

Les piles de récursivité les unes sur les autres

 function a() {a()}; // function calls itself until a stack overflow. 

La pile ressemble à ça

 a() a() a() a() ... until a crash. 

Avec setTimeout, vous exécutez une fonction. Cette fonction configure un événement pour que la fonction s'exécute à nouveau – mais voici la différence importante: la fonction sort, complètement et disparue [1]. Ensuite, on l'appelle à nouveau.

En règle, il n'est pas très différent de faire ceci:

 function a() {console.log("I am called");} a(); // Call the function; a(); // Call the function again a(); // Call the function again 

setTimeout donne simplement au navigateur la possibilité de «respirer» si vous voulez. Une chance pour l'écran de mettre à jour, d'autres événements à traiter. Il n'est pas nécessaire, pour utiliser la bonne terminologie, de block le navigateur.

Cela ne crée pas de fuite de mémoire.

En fait, c'est un concept assez utilisé. Habituellement, il apparaît sous cette forme:

 setTimeout(function next() { // Do something... // Have the function set another timeout to call itself later. setTimeout(next, 10); }, 10); 

Lorsque vous souhaitez vérifier quelque chose souvent (ici toutes les 10 millisecondes), il est préférable d'utiliser ce modèle au lieu de setInterval car il peut améliorer les performances de votre page. Par exemple, si votre fonction prend plus de 10 millisecondes à exécuter, et vous utilisez setInterval(f, 10) il sera continuellement appelé. Cependant, si vous utilisez le motif setTimeout ci-dessus, il vous assurera au moins que le processeur obtienne une rupture de 10 millisecondes entre chaque appel, peu importe la durée de votre fonction à exécuter.

Voir cette vidéo (à partir de 7h46) de Paul Irish pour plus d'informations sur ce modèle.