for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
},i * 1000 );
}
Buenas, en el siguiente trozo de código, el valor de i siempre es 5, en el console.log(). ¿Por qué?
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
},i * 1000 );
}
Buenas, en el siguiente trozo de código, el valor de i siempre es 5, en el console.log(). ¿Por qué?
Con este ejemplo se vería aproximadamente lo que ocurre con la función.
for (var i = 0; i < 5; i++) {
console.log(i);
setTimeout(function() {
console.log('TimeOut: ' + i);
},i * 1000 );
}
Lo único que hice fue colocar un console.log()
fuera de la función setTimeout
y añadirle un string
al que existe dentro de dicha función para diferenciar.
Si revisamos lo que nos escupe la consola podemos ver que primeramente se pinta:
14:55:01.689 0
14:55:01.689 1
14:55:01.689 2
14:55:01.689 3
14:55:01.689 4
Para posteriormente escupir:
14:55:01.691 TimeOut: 5
14:55:02.698 TimeOut: 5
14:55:03.690 TimeOut: 5
14:55:04.690 TimeOut: 5
14:55:05.696 TimeOut: 5
El tener activado el temporizador de la consola del navegador nos permite apreciar también en que momento se ejecuta cada salida por consola.
Revisando esto podemos ver que la función setTimeout
tarda más en lanzarse pro primera vez que lo que tarda el bucle en recorrerse entero, por lo que esta cuando accede a la variable i
ya se encuentra con valor 5.
Lo mejor para entender lo que pasa es reproducir la ejecución manualmente:
for (var i = 0; i < 5; i++) { // durante 5 veces ...
setTimeout(function() { // ... colocamos en la cola de eventos por tiempo...
console.log(i); // ... una función que imprime el valor de i ...
},i * 1000 ); // ... con un segundo entre cada función.
}
Y un segundo después, con i valiendo 5 (que es cuando el bucle ha terminado), se imprime su valor.
Y un segundo después, con i valiendo 5 (que es cuando el bucle ha terminado), se imprime su valor.
Y un segundo después, ... bueno, ya sabemos lo que pasa.
Esto ocurre porque se ha creado una clausura de función: i es una variable externa a la función anónima que se le ha pasado a setTimeout, por lo que se queda en memoria con el último valor existente.
¿Por qué se comparte i entre las 5 funciones?, bueno, hay que entender que el uso de var
conlleva un comportamiento especial: El compilador "sube" la declaración al inicio del bloque:
//equivalente a tu código
var i;
for (i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
},i * 1000 );
}
En cambio, si usases let
, en cada iteración estarías trabajando con una instancia distinta:
for (let i = 0; i < 5; i++) {
//en cada iteración se genera una variable i con un valor distinto!
setTimeout(function() {
console.log(Date(),'->',i); // es una variable distinta para cada función
},i * 1000 );
}
Otra solución, en caso de que no puedas usar let (no funciona en IE10), es usar lo siguiente:
for (var i = 0; i < 5; i++) {
setTimeout(function(param1, param2) {
console.log(param1, param2);
},i * 1000, 'Pasando 2 parámetros',i );
}
La función setTimeout
permite definir los parámetros con los que será llamada la función callback:
setTimeout(funcion[, retraso, parametro1, parametro2, ...]);
Para responder debes saber dos cosas:
.js
y las funciones asincronas la coloca en su cola de eventos; es decir que el codigo ejecutado en primera instancia es for (var i = 0; i < 5; i++){ }
y en la cola de eventos de Javascript queda cinco veces setTimeout(function() { console.log('TimeOut: ' + i); },i * 1000 );
var
esta tiene otro tipo de alcance. por el cual el ultimo valor sera el #5, y al ejecutar el setTimeout
desde la cola de eventos i ya tiene el valor # 5for (var i = 0; i< 5; i++){
}
console.log(i);
Modificare el código un poco , para fines practicos puedas entender del porque 5
function ejecutatimeOut(){
setTimeout(function() {
console.info(`TimeOut ${i}`);
},i * 1000 );
}
var i = 0;
for (i = 0; i < 5; i++) {
}
console.log("valor de i=" , i); // la variable ya esta en 5
ejecutatimeOut()
ejecutatimeOut()
ejecutatimeOut()
ejecutatimeOut()
ejecutatimeOut()
La solución a esta pregunta te invito a leer esta respuesta:
Al declarar la variable con var
, en el for
se está sobrescribiendo su valor. Prueba con un let
:
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
},i * 1000 );
}
Se puede hacer una función que devuelva otra función.
Esta función recibe el argumento i
, y se va transpasando a la función de adentro, aunque esa función, no recibe ningún argumento.
Por último, se le pasa el argumento para que esa función sea generada.
for (var i = 0; i < 5; i++) {
setTimeout( // La i se transpasa hacia abajo
(function(i){ // ----------+
return function(){ // ‖
console.log(i); // <--+
}
})(i), // Aquí se le pasa el argumento.
i * 1000 );
}