Note: Linters often complain when you put functions inside of loops, because the mistakes of not understanding closure are so common among developers. We explain how to do so properly here, leveraging the full power of closure. But that subtlety is often lost on linters and they will complain regardless, assuming you don’t actually know what you’re doing.

    The spirit of this code snippet is that we would normally expect for the behavior to be that the numbers “1”, “2”, .. “5” would be printed out, one at a time, one per second, respectively.

    In fact, if you run this code, you get “6” printed out 5 times, at the one-second intervals.

    Huh?

    Firstly, let’s explain where comes from. The terminating condition of the loop is when i is not <=5. The first time that’s the case is when i is 6. So, the output is reflecting the final value of the i after the loop terminates.

    This actually seems obvious on second glance. The timeout function callbacks are all running well after the completion of the loop. In fact, as timers go, even if it was setTimeout(.., 0) on each iteration, all those function callbacks would still run strictly after the completion of the loop, and thus print 6 each time.

    But there’s a deeper question at play here. What’s missing from our code to actually have it behave as we semantically have implied?

    Put that way, of course all functions share a reference to the same i. Something about the loop structure tends to confuse us into thinking there’s something else more sophisticated at work. There is not. There’s no difference than if each of the 5 timeout callbacks were just declared one right after the other, with no loop at all.

    OK, so, back to our burning question. What’s missing? We need more cowbell closured scope. Specifically, we need a new closured scope for each iteration of the loop.

    We learned in Chapter 3 that the IIFE creates scope by declaring a function and immediately executing it.

    Let’s try:

    1. for (var i=1; i<=5; i++) {
    2. setTimeout( function timer(){
    3. console.log( i );
    4. }, i*1000 );
    5. }

    Does that work? Try it. Again, I’ll wait.

    I’ll end the suspense for you. Nope. But why? We now obviously have more lexical scope. Each timeout function callback is indeed closing over its own per-iteration scope created respectively by each IIFE.

    It’s not enough to have a scope to close over if that scope is empty. Look closely. Our IIFE is just an empty do-nothing scope. It needs something in it to be useful to us.

    Eureka! It works!

    A slight variation some prefer is:

    1. for (var i=1; i<=5; i++) {
    2. (function(j){
    3. setTimeout( function timer(){
    4. console.log( j );
    5. }

    Of course, since these IIFEs are just functions, we can pass in i, and we can call it j if we prefer, or we can even call it i again. Either way, the code works now.

    The use of an IIFE inside each iteration created a new scope for each iteration, which gave our timeout function callbacks the opportunity to close over a new scope for each iteration, one which had a variable with the right per-iteration value in it for us to access.

    Problem solved!

    Look carefully at our analysis of the previous solution. We used an IIFE to create new scope per-iteration. In other words, we actually needed a per-iteration block scope. Chapter 3 showed us the let declaration, which hijacks a block and declares a variable right there in the block.

    It essentially turns a block into a scope that we can close over. So, the following awesome code “just works”:

    1. for (let i=1; i<=5; i++) {
    2. setTimeout( function timer(){
    3. console.log( i );
    4. }

    How cool is that? Block scoping and closure working hand-in-hand, solving all the world’s problems. I don’t know about you, but that makes me a happy JavaScripter.