0

How does hoisting explain this behavior?

for(var i = 0; i < 4; i++) {
    setTimeout(function() {
        alert("i is: " + i);
    }, i * 200);
}

The output of this is 4, 4, 4, 4.

This is used as a dangerous example of hoisting often in literature. It makes sense that the latter outputs may be 4 since the i variable is bound to function scope and as such is shared between all calls and by the time they execute i will be 4 having finished the for loop. However, the initial call specifies a timeout of 0 * 200 or 0 and as such I feel this should execute immediately while i is still less than 4. What causes all the output from this function to be 4's?

majic bunnie
  • 1,395
  • 2
  • 10
  • 21
  • 4
    It doesn't because it has nothing to do with hoisting. And even if you pass `0` as delay, the minimal delay in browsers is about 10ms. – Felix Kling Feb 22 '13 at 19:18
  • I figured as much, the examples I have been reading online are misleading. I.e. http://thecomputersarewinning.com/post/a-dangerous-example-of-javascript-hoisting/ – majic bunnie Feb 22 '13 at 19:21

3 Answers3

1

It doesn't because it has nothing to do with hoisting.

And even if you pass 0 as delay, the minimal delay in browsers is about 10ms. Whatever delay you pass, the function is never executed immediately.

From the MDN documentation:

It's important to note that if the function or code snippet cannot be executed until the thread that called setTimeout() has terminated.

So, before any of the callbacks is called, the for loop already terminated.

Related questions:

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
1

"However, the initial call specifies a timeout of 0 * 200 or 0 and as such I feel this should execute immediately..."

It could be that that may happen in an environment that can take advantage of multiple threads, but in a single threaded environment, the current synchronous flow of execution will continue unfettered until complete.

This means that the loop continues and finishes before the first setTimeout callback happens.

Even if your loop took 10,000 milliseconds to complete, the loop will be allowed to continue before the first callback would fire.


Try this demo:

var start = Date.now();

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

     // set up the first timeout
    setTimeout(function() {
        alert("i is: " + i);
    }, i * 200);

    while (Date.now() - start < 1000) ; // block for 1000 ms
}

After the first setTimeout has been set up, we block for 1000ms. The result of the alert() calls is still the same. This is because the synchronous flow continues irrespective of any asynch code that is scheduled to run.

the system
  • 9,244
  • 40
  • 46
0

The four calls to setTimeout fire within the loop before the first timer expires (and the anonymous method call fires). As a result, when it does fire, the result is always 4. setTimeout timer delays are never guaranteed.

David W
  • 10,062
  • 34
  • 60