Thursday 17 November 2016

Javascript: Closure + Loop

What does this program print out?
for (var i=1; i<=5; i++) {
    setTimeout( function timer(){
        console.log( i );
    }, i*1000 );
}
At first glance, it seems to print 1,2,3,4,5, at 1 second interval. But in fact, it prints 6,6,6,6,6 at 1 second interval. What is going on?

At second glance, it does make sense. The timer() function is invoked after the waiting period (i*1000). By the time it starts to execute, the for loop has long finished. And at this point of time, i = 6.

So how to fix it? 

By wrapping the setTimeout() with a function makes the variable j local to the enclosing function.

for (var i=1; i<=5; i++) {
    (function(){
        var j = i;
 setTimeout( function timer(){
     console.log(j);
        }, i*1000 );
    })();
}
A neater variation.
for (var i=1; i<=5; i++) {
    (function(j){
 setTimeout( function timer(){
     console.log(j);
        }, i*1000 );
    })(i);
}
Even better is replace var with let (ES6 feature)
for (let i=1; i<=5; i++) {
    setTimeout( function timer(){
 console.log(i);
    }, i*1000 );
}
This is most surprising. It turns out let not only restrain the variable within the for loop but also each iteration!