4

The following code uses a for-loop to add values to an array. Each time the current state of the array is logged in the console. Then a setTimeout function is used to create a delay of half a second between the outputs.

But the delayed output always shows the whole array with the status after it passed the whole for loop and not with the status when the setTimeout was invoked.

var my_array = [];

for (var i = 0; i < 10; i++){
  my_array[i] = "Nr " + i;

  console.log(my_array);    
  setTimeout(function(par) { console.log(par); }, 500*i, my_array);

}

How can that behavious be explained? Consider the follwing code, which is different because it sends the i-variable to the callback (instead of the array):

for (var i = 0; i < 10; i++){

  console.log(i);
  setTimeout(function(par) { console.log(par); }, 500*i, i);

}

This snippet logs the i-variable in a delayed output with its value of the time the setTimeout was invoked. As expected, it does not log the i-variable with the value it has after the for loop.

Anton Harald
  • 5,772
  • 4
  • 27
  • 61

4 Answers4

2

In the half second delay the original array is changed so the hole array will be displayed. You have to clone the array for the desired effect

Try changing

setTimeout(...); 

to

(function (arr) {
    setTimeout(function(par) { console.log(par); }, 500*i, arr);
})(my.slice(0));

With the my.slice(0) you clone the entire array with the current state, so there can't be any changes to it

Update:

setTimeout(function(par) { console.log(par); }, 500, my_array.slice(0));

removed the *i (you wanted half a second delay) and removed the closure (unnecessary). Still working perfectly: https://jsfiddle.net/wuL3a52x/

0

Try this:

<script>
  var my = [];
  for(var i = 0; i < 10; i++)
  {
    my.push("Nr " + i);
    var myArr=my.slice(0);
    setTimeout(function(x)
    {
      return function()
      {
        console.log(x);
      };
    }(myArr), 1000 * i);
  }
</script>
Rayon
  • 36,219
  • 4
  • 49
  • 76
0

That's happening because setTimeout use a reference to myand by the time the first callback is executed the cycle has ended already.

You can use slice to create a copy of the array each cycle to make it immutable and pass it to setTimeout

var my = [1,2,3,4,5,6,7,8,9,10];

for (var i = 0; i < 10; i++){
  my[i] = "Nr " + i;

  setTimeout(function(par) { console.log(par); }, 500*i, my.slice());

}
SharpEdge
  • 1,782
  • 1
  • 10
  • 20
0

You could solve this with async/await:

 function timer(ms) {
     return new Promise(res => setTimeout(res, ms));
 }

 for (var i = 0; i < myArray.length; i++){
     console.log(myArray[i]);
     await timer(500);
 }
Elliott
  • 2,035
  • 20
  • 23