J'ai ce script :
for (var i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}
Mais 3
est alerté les deux fois, au lieu de 1
puis 2
.
Y a-t-il un moyen de passer i
, sans écrire la fonction comme une chaîne de caractères ?
J'ai ce script :
for (var i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}
Mais 3
est alerté les deux fois, au lieu de 1
puis 2
.
Y a-t-il un moyen de passer i
, sans écrire la fonction comme une chaîne de caractères ?
Vous devez organiser une copie distincte de "i" pour être présente pour chacune des fonctions timeout.
function doSetTimeout(i) {
setTimeout(function() {
alert(i);
}, 100);
}
for (var i = 1; i <= 2; ++i)
doSetTimeout(i);
Si vous ne faites pas quelque chose comme ceci (et il existe d'autres variations sur cette même idée), alors chacune des fonctions de gestionnaire de minuterie partagera la même variable "i". Quelle est la valeur de "i" une fois la boucle terminée? C'est 3! En utilisant une fonction intermédiaire, une copie de la valeur de la variable est faite. Puisque le gestionnaire de délai est créé dans le contexte de cette copie, il possède sa propre "i" privée à utiliser.
Éditer:
Il y a eu quelques commentaires au fil du temps où une certaine confusion était évidente sur le fait que la mise en place de quelques temporisations provoquait le déclenchement de tous les gestionnaires en même temps. Il est important de comprendre que le processus de mise en place du minuteur — les appels à
setTimeout()
— prend presque aucun temps du tout. Autrement dit, dire au système, "Veuillez appeler cette fonction après 1000 millisecondes" reviendra presque immédiatement, car le processus d'installation de la demande de délai dans la file d'attente du minuteur est très rapide.Ainsi, si une suite de demandes de délai est effectuée, comme c'est le cas dans le code dans la question initiale et dans ma réponse, et que la valeur de retard de temps est la même pour chacune, alors une fois ce montant de temps écoulé, tous les gestionnaires de minuterie seront appelés les uns après les autres en succession rapide.
Si ce dont vous avez besoin est que les gestionnaires soient appelés à intervalles, vous pouvez soit utiliser
setInterval()
, qui est appelé exactement commesetTimeout()
mais qui se déclenchera plus d'une fois après des retards répétés du montant demandé, ou bien vous pouvez établir les temporisations et multiplier la valeur d'attente par votre compteur d'itération. Autrement dit, pour modifier mon code exemple:function doScaledTimeout(i) { setTimeout(function() { alert(I); }, i * 5000); }
(Avec un délai de
100
millisecondes, l'effet ne sera pas très évident, alors j'ai augmenté le nombre à 5000.) La valeur dei
est multipliée par la valeur de base du retard, donc appeler cela 5 fois dans une boucle donnera des retards de 5 secondes, 10 secondes, 15 secondes, 20 secondes et 25 secondes.
Mise à jour
Ici en 2018, il existe une solution alternative plus simple. Avec la nouvelle capacité de déclarer des variables dans des portées plus étroites que les fonctions, le code original fonctionnerait si modifié comme suit:
for (let i = 1; i <= 2; i++) {
setTimeout(function() {
alert(i)
}, 100);
}
La déclaration let
, contrairement à var
, entraînera elle-même l'existence d'un i
distinct pour chaque itération de la boucle.
Ceci est la méthode préférée car elle ne provoque pas de définition de fonction à l'intérieur du corps de la boucle. Les autres fonctionneront, mais ne sont pas préférables (même si elles montrent l'incroyable côté bad-ass de JS ;) ).
@JAAulde J'avoue que personnellement, je le ferais avec une fonction anonyme, mais de cette manière est plus jolie en tant qu'exemple.
Je préfère personnellement les fonctions anonymes à cela car je ne veux pas configurer tout un tas de noms. Trop paresseux pour y penser.
Vous pouvez utiliser une expression de fonction immédiatement invoquée (IIFE) pour créer une fermeture autour de setTimeout
:
for (var i = 1; i <= 3; i++) {
(function(index) {
setTimeout(function() { alert(index); }, i * 1000);
})(i);
}
IIFE n'est en rien qu'un raccourci pratique pour une fonction anonyme et une exécution immédiate - C'est en réalité ce que fait la réponse acceptée, en étapes complètes, sans raccourci - enveloppe l'appel de fonction dans une autre fonction, de sorte que la fonction interne obtienne une copie locale de l'argument de fonction externe.!!!
Le paramètre de fonction à setTimeout
est en train de fermer sur la variable de boucle. La boucle se termine avant le premier délai et affiche la valeur actuelle de i
, qui est 3
.
Parce que les variables JavaScript ont uniquement portée de fonction, la solution est de passer la variable de boucle à une fonction qui définit le délai. Vous pouvez déclarer et appeler une telle fonction comme ceci:
for (var i = 1; i <= 2; i++) {
(function (x) {
setTimeout(function () { alert(x); }, 100);
})(i);
}
Pour fonctionner, il suffit de multiplier le délai par i. comme ceci : setTimeout(function () { alert(x); }, i*100);
Vous avez juste besoin de remplacer var par le mot-clé let et cela affichera le nombre 1 puis 2. Mais voici le piège une fois de plus, cela affichera à la fois 1 et 2 juste après 2 secondes. Si vous voulez afficher 1 et 2 à un intervalle de 1 seconde chacun, alors dans le rappel setTimeout, modifiez 1000 en i * 1000
Vous pouvez utiliser les arguments supplémentaires pour setTimeout pour passer des paramètres à la fonction de rappel.
for (var i = 1; i <= 2; i++) {
setTimeout(function(j) { alert(j) }, 100, i);
}
Remarque : Cela ne fonctionne pas sur les navigateurs IE9 et précédents.
Il existe un polyfill pour ce problème IE ici; developer.mozilla.org/fr/docs/Web/API/WindowTimers/…
RÉPONSE?
Je l'utilise pour une animation d'ajout d'articles dans un panier - une icône de panier flotte jusqu'à la zone de panier depuis le bouton "ajouter" du produit, lorsque vous cliquez :
function addCartItem(opts) {
for (var i=0; i
`
NOTEZ que la durée est en unités de temps n épochs.
Donc, en commençant au moment du clic, le départ de l'époque des animations (de CHAQUE animation) est le produit de chaque unité d'une seconde multiplié par le nombre d'articles.
époque : https://en.wikipedia.org/wiki/Epoch_(reference_date)
J'espère que cela vous aide !
`
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.
9 votes
Aucune des réponses ici ne fonctionne. Chacune d'entre elles se contente de retarder pendant le temps défini, puis exécute immédiatement la boucle entière sans autres retards. En examinant le code de l'OP, il est clair qu'ils voulaient un retard à chaque itération.
1 votes
Il convient également de noter que si l'utilisateur SOUHAITAIT que les alertes se déclenchent en même temps, la mise en place de plusieurs
setTimeout
n'est PAS la meilleure façon de le faire.14 votes
Utilisez le mot-clé "let" au lieu de var, cela résoudra le problème.
9 votes
Je tentais quelque chose de similaire et personne n'était capable de répondre à la question, ou d'expliquer ce que je faisais de travers, conceptuellement. Voici ce que vous devez comprendre, probablement: setTimeout() est asynchrone: Le moteur JS n'attendra pas n millisecondes (100 dans votre exemple), avant de continuer. Il se contente de prendre une 'note mentale': "Après 100 ms, exécute (dans ce cas) l'alerte", et continue d'exécuter la boucle. Il effectue les 3 (ou 300) itérations avant que les 100 ms soient écoulées, donc éventuellement, lorsque ce temps s'écoule, il affiche toutes les 3 (ou 300) alertes en même temps.
2 votes
Je pense que vous pouvez utiliser
let
au lieu devar
. Cela va résoudre votre problème.2 votes
Comment régler le problème ? Pouvez-vous clarifier ?
0 votes
@ChuckLeButt oui tu as raison, voici un code qui fonctionne réellement : function easyLoopLimiter(loopStrtNum, loopEndNum, timeToPause, yourFunction) { function loop(i) { if (i{i++;loop(i)}, timeToPause); } else {return;} } setTimeout(()=>{loop(loopStrtNum)}, timeToPause); } /* Ici un exemple */ easyLoopLimiter(0, 10, 1000, "alert(i); console.log(i)")
0 votes
Ne pas non plus essayer dans la console "Nouvel onglet" car cela générera une erreur en raison de la sécurité de Google
0 votes
@Sevada797 Peut-être l'ajouter en tant que réponse?