0

Can someone explain me that:

const array = [0, 1, 2, 4, 8, 16, 32, 16, 8, 4, 2, 1, 0];

for (let i = 0; i < array.length; i++) {
    setTimeout(function () {
        console.log(array[i]);
    }, array[i])
}

Why this return 0 1 1 0 2 2 4 4 8 8 16 16 32, instead of array items one by one?

M.A.K. Ripon
  • 2,070
  • 3
  • 29
  • 47
Mateusz Daniluk
  • 115
  • 2
  • 9
  • setTimeout is asynchronus – Manos Kounelakis Oct 06 '18 at 14:07
  • https://stackoverflow.com/questions/5226285/settimeout-in-for-loop-does-not-print-consecutive-values – Manos Kounelakis Oct 06 '18 at 14:07
  • 3
    Possible duplicate of [setTimeout in for-loop does not print consecutive values](https://stackoverflow.com/questions/5226285/settimeout-in-for-loop-does-not-print-consecutive-values) – Dexygen Oct 06 '18 at 14:08
  • 1
    @GeorgeJempty Not a duplicate. OP uses `let`. – Ivar Oct 06 '18 at 14:10
  • 2
    The delays are all different based on value of `array[i]`. What exactly are you wanting to accomplish? – charlietfl Oct 06 '18 at 14:13
  • @Ivar I've retracted close vote but somebody needs to prove this makes a difference – Dexygen Oct 06 '18 at 14:13
  • @Ivar The fact that OP uses `let` does not reflect the behavior of `setTimeout`. Anyway, I don't see if this is any different than the marked duplicated post. – choz Oct 06 '18 at 14:20
  • 1
    It makes a difference that `let` binds to a separate value when it is inside a closure. Using `var` would cause `undefined` to be printed because at the time it logs, it the `i` will be equal to the array lenght. (And since, arrays are zero-based, will try to fetch an element that is not there.) – Ivar Oct 06 '18 at 14:21
  • 4
    The problem here is just that the timeout length is the value of the element, so of course the higher values are logged later, because the timeout is longer. – Ivar Oct 06 '18 at 14:22
  • So, to the OP - if you wanted the array items to alert in sequence, you probably want the second parameter (the timer) of `setTimeout` to be `i * 1000` (to get one value every second), not `array[i]` – Robin Zigmond Oct 06 '18 at 14:24
  • @Ivar Ah. I got your point there. It's correct that `let` does bind to the scope inside `setTimeout` closure. But what I was actually trying to point out, is the `delay` in that `setTimeout` since I think what OP is trying to ask is to print them in their normal order – choz Oct 06 '18 at 14:28

4 Answers4

5

The numbers are appearing one after another. However you won't be able to see that because you have set a very low delay between them. Some of them will appear together. Those are the ones having the same value for array[i].

For better seeing the delay between them, I would suggest replacing array[i] by array [i] * x, where x is a number. Look at this:

const array = [0, 1, 2, 4, 8, 16, 32, 16, 8, 4, 2, 1, 0];

for (let i = 0; i < array.length; i++) {
    setTimeout(function () {
        console.log(array[i]);
    }, array[i] * 500)
}

0 will be echoed after 0 × 0.5 = 0 seconds.

1 will be echoed after 1 × 0.5 = 0.5 seconds.

2 will be echoed after 2 × 0.5 = 1 seconds.

4 will be echoed after 4 × 0.5 = 2 seconds, and so on.

EDIT: If you are asking about the reason why 0 1 1 0 is logged instead of 0 0 1 1, it is likely because the computer takes some small time looping through the array, and before it reaches the final "0", 1 millisecond would have passed, so it will be time to log "1" and "1". Notice that increasing the delay between the logs solves the problem.

Wais Kamal
  • 5,858
  • 2
  • 17
  • 36
  • Numbers are not appearing one after another(just run code snippet that Mateusz Daniluk provided and look at result) - in case of first 4 (0,1,1,0) the order is changed. – agsigma Oct 06 '18 at 14:41
0

When setTimeout()-ing, 0 and 1 mean practically the same thing for a browser (that's why your zeros and ones are mixed at the beginning). All the other numbers are correctly showing up with the delay you're giving them, so first the smallest (and they get scheduled first), then the 2, 4, etc, these will appear later (only a very little bit later) on the console. If you slow this thing down, as @WaisKamal suggested, this timed order will be obvious. So by the time your second zero appears, all the other outputs are already scheduled (that is, setTimeouts themselves all happened, in the order of the array), and then they run in the order of timing.

dkellner
  • 8,726
  • 2
  • 49
  • 47
0

It is because the execution of the loop , is faster than your setTimeout function delay, so that's why the number with minimum delay executes first.

Beri
  • 11,470
  • 4
  • 35
  • 57
0

Your code should log array elements in increasing order:

0,0,1,1,2,2,3,3,4,4,16,16,32.

And it would if you changed timeout to larger values:

const array = [0, 100, 200, 400, 800, 1600, 3200, 1600, 800, 400, 200, 100, 0];

for (let i = 0; i < array.length; i++) {
    setTimeout(function () {
        console.log(array[i]);
    }, array[i])
}

The reason why your code doesn't work as expected is order in which setTimeout callbacks are executed.

You can think of it this way - after timeout defined in setTimeout has passed the callback is added to the some kind of queue, that runs it as quickly as possible(remember that js is single threaded). In your case callback for last element of array is added to that queue after callback for first, second and third element(because your loop execution takes more than 1 millisecond).

agsigma
  • 321
  • 1
  • 8