Immediate Execution

JavaScript functions are versatile. They are powerful and flexible and exotic like a carnival contortionist. It’s amazingly easy to get into trouble with them. Not just small trouble, but big-time Pinocchio-hanging-out-with-Lampwick trouble.

Here’s a great way to get sucked into some such trouble:

var i;
for (i=0;i<5;i++) {
    setTimeout(function() {alert('The number is '+i);},
               5000);
}

I think most programmers would expect this code to give you five alerts, and that the alerts would look like this:

The number is 0
The number is 1
The number is 2
The number is 3
The number is 4

But that’s not how it works. Instead, you get this:

The number is 5
The number is 5
The number is 5
The number is 5
The number is 5

What’s happening?

The inside function (the one in the setTimeout call) is a closure. A closure has access to its parent function’s variables, even after the parent function is done. In this case, we’re setting up five alerts. This happens very fast, before the first alert has executed. So by the time the alerts happen, the loop counter is 5.

So how do you fix this?

Well, the first answer is that it’s a dumb way to do it. You can pass setTimeout a string instead of a function and solve it that way, or just call one setTimeout and have that one call the next one.

But I’ve had a problem similar to this with click handlers on a series of buttons, so let’s go ahead and look at a real solution.

for (i=0;i<5;i++) {
  (function(num) {
    setTimeout(function() {alert('second '+num);},5000);
  })(i);
}

Huh? That looks crazy. Let’s figure it out.

Here’s a function.

function lert() {
    alert("Nope");
}

Here’s another function.

(function () {
    alert("Yep");
})();

What are the differences between them?

  • The first function has a name (the name is “lert”), while the second function does not have a name.
  • The first function does not execute until it’s called, but the second function executes immediately.

To get the immediate execution, we wrap the function in parentheses and follow it with another pair of parens. That’s just how it is–get used to it.

That’s how our solution works, except it needs to be a bit fancier because we need to pass a parameter in.