Mon objectif est un diaporama d'images d'arrière-plan avec HTML / CSS / JS. De nombreuses solutions que j'ai trouvées font la promotion de quelque chose comme ceci:
my_recursion(); function my_recursion () { // cycle the Background image ... setTimeout(my_recursion, 3000); }
Ai-je tort de supposer que c'est un mauvais style? Je m'attendrais à ce que, par exemple, cycle 1000 toutes les 999 autres instances de my_recursion sont toujours ouvertes / sur la pile? Cela ne crée-t-il pas une pile infinie qui consomme de plus en plus de mémoire?
Ou y a-t-il une sorte d'intelligence impliquée qui fait quelque chose comme "si une fonction s'appelle à la fin, le (n-1) La fonction est détruite, y compris toutes les variables qui lui ont été assignées "?
3 Réponses :
Cela n'entraînera pas une augmentation infinie de la pile, à cause du fonctionnement de setTimeout, et à mon avis, ce n'est pas un mauvais style.
setTimeout
ne garantit pas que le code s'exécutera directement après le délai imparti. Au lieu de cela, après ce délai, il poussera le rappel sur une "file d'attente", qui sera traitée lorsque la pile sera vide. Donc, il ne fonctionnera que lorsque my_recursion sera de retour et que la pile sera vide.
Si une fonction s'appelle à la fin (...)
my_recursion
ne s'appelle nulle part. Il se passe simplement comme argument à setTimeout
. Après cela, il continuera simplement à s'exécuter, reviendra directement après et sera sorti de la pile.
Cette présentation explique la pile et la file d'attente des événements.
Cela signifie-t-il que si j'avais du code juste après setTimeout
qui prenait plus de 3 secondes à s'exécuter (par exemple 4 secondes), la fonction suivante commencerait à s'exécuter en parallèle pendant ma (n-1) ème instance est toujours en cours d'exécution? Ou cela signifie-t-il que l'instance suivante démarre après 4 secondes car elle n'a pas pu démarrer après 3 puisque la (n-1) ème fonction était toujours en cours d'exécution?
Je ne pense pas qu'il soit nécessaire que la fonction s'appelle elle-même immédiatement et explicitement pour qu'elle soit récursive. Tant qu'elle aboutit à une autre exécution de la même fonction, elle doit être récursive. Si seuls les appels directs et explicites étaient requis, alors l'optimisation des appels de fin (TCO) serait importante pour déterminer si une fonction est récursive ou non.
@Robert: Non, le code ne s'exécuterait pas en parallèle. setTimeout
ne garantit pas que le code s'exécutera directement après le délai imparti. Au lieu de cela, après ce délai, il poussera le rappel sur une "file d'attente", qui sera traitée lorsque la pile sera vide. Il ne fonctionnera donc que lorsque my_recursion
sera de retour et que la pile sera vide.
J'ai ajouté un lien vers une présentation à JSConf qui explique cela.
@vlaz: Vous avez raison, j'ai mis à jour ma réponse pour qu'elle soit plus précise.
Le code est correct. Il détruit toutes les variables car lorsque vous l'appelez pour la première fois. Il setTimeout ()
pour la fonction
suivante et au dernier retour. Votre fonction ne retourne
pas la suivante.
my_recursion(); function my_recursion () { // cycle the Background image ... setTimeout(my_recursion, 3000); //Sets timeout for next function. //returns undefined here }
Dans votre question, votre fonction n'a aucun paramètre. Dans une implémentation réelle, j'espère que vous prévoyez de les utiliser.
<p>Wait 3 seconds...</p>
p { text-align: center; font-size: 3vw; font-weight: bold; color: white; }
const cycleBackground = (elem, bgs = [], ms = 1e3, i = 0) => ( elem.setAttribute ('style', bgs[i]) , setTimeout ( cycleBackground // function to schedule , ms // when to schedule, ms from now , elem // user-specified element to change , bgs // user-specified backgrounds , ms // user-specified delay , (i + 1) % bgs.length // next background index ) ) const backgrounds = [ "background-color: red;" , "background-image: linear-gradient(45deg, cyan 0%, purple 75%);" , "background-color: green;" ] // call site cycleBackground ( document.body // element to target , backgrounds // list of backgrounds , 3e3 // delay, 3 seconds )
Il n'y a qu'une seule entrée de
my_recursion
sur la pile. La première exécution se termine complètement avant le lancement de la seconde.Probablement pas exactement une dupe, mais j'ai déjà écrit sur la pile d'appels, la récursivité et
setTimeout
(en tant que mécanisme pour interagir avec la file d'attente) avantMais pourquoi? Cela signifie-t-il qu'un
var x = 1
juste après monsetTimeout
ne sera jamais exécuté?@Robert les appels à
setTimeout ()
reviennent immédiatement. Le système garde la trace de la minuterie en attente et appelle la fonction de rappel le moment venu.@Robert il serait exécuté mais
setTimeout
planifiera la prochaine exécution après la fin de celle en cours en la plaçant dans la file d'attente.D'accord, je vois.
setTimeout
revient immédiatement. Je pensais qu'il attendrait 3 secondes, puis appellerait la fonction, puis reviendrait à la fonction d'origine. Mais que se passe-t-il si après 3 secondes, ma fonction d'origine est toujours en cours d'exécution parce qu'il y a des calculs compliqués après l'appelsetTimeout
? J'ai posté la même question sous la réponse ci-dessous.@Robert non, ce n'est pas possible. Je vous exhorte à jeter un coup d'œil à l'autre question que j'ai liée et potentiellement à examiner davantage la file d'attente des événements. Si votre fonction est toujours en cours d'exécution dans 3s, rien d'autre ne fonctionnera . Ce n'est qu'à la fin que tout autre code planifié s'exécutera - vous n'obtiendrez pas deux exécutions parallèles.
D'accord merci, cela a du sens. Cette réponse a également aidé. Tout est poussé dans la file d'attente et exécuté à l'heure planifiée SI tout ce qui a été planifié avant la fin. Il s'agit donc d'un délai d'exécution MINIMUM tel que mentionné dans la réponse liée. Donc ce n'est pas une mauvaise pratique :) Merci!
@Robert exactement - l'essentiel ici est de comprendre la file d'attente. Le délai que vous ajoutez pour
setTimeout
est en fait plus une suggestion et le délai réel que vous obtenez peut différer.De plus, la fonction n'est pas appelée de manière récursive, mais plutôt de manière répétitive. Le résultat serait le même que d'appeler la fonction plusieurs fois de manière séquentielle (sans délai dans ce cas bien sûr). Mais dans votre cas, la séquence d'appel est sans fin, sauf si vous ajoutez une condition avant
setTimeout
, ou effacez le timeout quelque part (ce qui n'est actuellement pas possible, puisque leid
du timeout n'est stocké nulle part).