javascript - with - setTimeout en for-loop no imprime valores consecutivos
settimeout typescript (10)
Esta pregunta ya tiene una respuesta aquí:
Tengo este guión:
for (var i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}
Pero 3
es alertado en ambas ocasiones, en lugar de 1
luego 2
.
¿Hay una manera de pasar i
, sin escribir la función como una cadena?
¡Esto es porque !
- Las devoluciones de llamada de la función de tiempo de espera se ejecutan bien después de completar el ciclo. De hecho, a medida que pasan los temporizadores, incluso si se establece setTimeout (.., 0) en cada iteración, todas esas funciones de devolución de llamada aún se ejecutarán estrictamente después de completar el bucle, ¡por eso se reflejaron 3!
- las dos funciones, aunque se definen por separado en cada iteración de bucle, están cerradas en el mismo ámbito global compartido , que, de hecho, tiene solo una i en él.
La Solución declara un solo alcance para cada iteración mediante el uso de una función automática ejecutada (anónima o mejor IIFE ) y tiene una copia de i , como esta:
for (var i = 1; i <= 2; i++) {
(function(){
var j = i;
setTimeout(function() { console.log(j) }, 100);
})();
}
el más limpio sería
for (var i = 1; i <= 2; i++) {
(function(i){
setTimeout(function() { console.log(i) }, 100);
})(i);
}
El uso de un IIFE (función IIFE ) dentro de cada iteración creó un nuevo alcance para cada iteración, lo que dio a nuestra función de tiempo de espera las devoluciones de llamadas la oportunidad de cerrar sobre un nuevo alcance para cada iteración, uno que tenía una variable con el derecho adecuado. valor de iteración en ella para que podamos acceder.
¿ RESPUESTA ?
Lo estoy usando para una animación para agregar artículos a un carrito: cuando se hace clic, el ícono del carrito flota en el área del carrito desde el botón "agregar" del producto:
function addCartItem(opts) {
for (var i=0; i<opts.qty; i++) {
setTimeout(function() {
console.log(''ADDED ONE!'');
}, 1000*i);
}
};
NOTA la duración es en unidades por epocs.
Entonces, comenzando en el momento del clic, las animaciones comienzan epoc (de CADA animación) es el producto de cada unidad de un segundo multiplicada por la cantidad de elementos.
epoc : https://en.wikipedia.org/wiki/Epoch_(reference_date)
¡Espero que esto ayude!
Bueno, otra solución de trabajo basada en la respuesta de Cody pero un poco más general puede ser algo como esto:
function timedAlert(msg, timing){
setTimeout(function(){
alert(msg);
}, timing);
}
function yourFunction(time, counter){
for (var i = 1; i <= counter; i++) {
var msg = i, timing = i * time * 1000; //this is in seconds
timedAlert (msg, timing);
};
}
yourFunction(timeInSeconds, counter); // well here are the values of your choice.
Debe disponer que una copia distinta de "i" esté presente para cada una de las funciones de tiempo de espera.
function doSetTimeout(i) {
setTimeout(function() { alert(i); }, 100);
}
for (var i = 1; i <= 2; ++i)
doSetTimeout(i);
Si no haces algo como esto (y hay otras variaciones en esta misma idea), entonces cada una de las funciones del controlador de temporizador compartirá la misma variable "i". Cuando el bucle termina, ¿cuál es el valor de "i"? ¡Es 3! Al utilizar una función de intermediación, se crea una copia del valor de la variable. Dado que el controlador de tiempo de espera se crea en el contexto de esa copia, tiene su propia "i" privada para usar.
Edición : ha habido un par de comentarios a lo largo del tiempo en los que se evidenció cierta confusión sobre el hecho de que la configuración de algunos tiempos de espera hace que los manejadores disparen al mismo tiempo. Es importante comprender que el proceso de configuración del temporizador (las llamadas a setTimeout()
lleva mucho tiempo. Es decir, decirle al sistema que "Por favor llame a esta función después de 1000 milisegundos" regresará casi inmediatamente, ya que el proceso de instalación de la solicitud de tiempo de espera en la cola del temporizador es muy rápido.
Por lo tanto, si se realiza una sucesión de solicitudes de tiempo de espera, como ocurre en el código en el OP y en mi respuesta, y el valor del retardo de tiempo es el mismo para cada una, entonces una vez que haya transcurrido ese tiempo, todos los controladores del temporizador Se llamarán uno tras otro en rápida sucesión.
Si lo que necesita es que los manejadores sean llamados a intervalos, puede usar setInterval()
, que se llama exactamente como setTimeout()
pero que se activará más de una vez después de repetidos retrasos de la cantidad solicitada, o en su lugar puede establecer los tiempos de espera y multiplique el valor de tiempo por su contador de iteración. Es decir, para modificar mi código de ejemplo:
function doScaledTimeout(i) {
setTimeout(function() {
alert(i);
}, i * 5000);
}
(Con un tiempo de espera de 100
milisegundos, el efecto no será muy obvio, por lo que alcancé el número hasta 5000.) El valor de i
se multiplica por el valor de retardo base, por lo que llamar 5 veces en un bucle dará lugar a demoras. de 5 segundos, 10 segundos, 15 segundos, 20 segundos y 25 segundos.
El argumento de la función para setTimeout
está cerrando sobre la variable de bucle. El bucle termina antes del primer tiempo de espera y muestra el valor actual de i
, que es 3
.
Debido a que las variables de JavaScript solo tienen un alcance de función, la solución es pasar la variable de bucle a una función que establece el tiempo de espera. Puede declarar y llamar a una función como esta:
for (var i = 1; i <= 2; i++) {
(function (x) {
setTimeout(function () { alert(x); }, 100);
})(i);
}
La solución real está aquí, pero debe estar familiarizado con el lenguaje de programación PHP. debe mezclar los pedidos de PHP y JAVASCRIPT para alcanzar su propósito.
presta atención a esto:
<?php
for($i=1;$i<=3;$i++){
echo "<script language=''javascript'' >
setTimeout(function(){alert(''".$i."'');},3000);
</script>";
}
?>
Hace exactamente lo que usted quiere, pero tenga cuidado de cómo hacer la relación entre las variables de PHP y las de JAVASCRIPT.
Podrías usar el método de bind
for (var i = 1, j = 1; i <= 3; i++, j++) {
setTimeout(function() {
alert(this);
}.bind(i), j * 100);
}
Puede usar los argumentos adicionales para establecer el tiempo de espera para pasar parámetros a la función de devolución de llamada.
for (var i = 1; i <= 2; i++) {
setTimeout(function(j) { alert(j) }, 100, i);
}
Nota: Esto no funciona en los navegadores IE9 y anteriores.
Puede usar una expresión de función invocada de inmediato ( IIFE ) para crear un cierre alrededor de setTimeout
:
for (var i = 1; i <= 3; i++) {
(function(index) {
setTimeout(function() { alert(index); }, i * 1000);
})(i);
}
Tuve el mismo problema una vez que así es como lo resolví.
Supongamos que quiero 12 retrasos con un intervalo de 2 segundos
function animate(i){
myVar=setTimeout(function(){
alert(i);
if(i==12){
clearTimeout(myVar);
return;
}
animate(i+1)
},2000)
}
var i=1; //i is the start point 1 to 12 that is
animate(i); //1,2,3,4..12 will be alerted with 2 sec delay