Practicing Loops and Closures

I was perusing through some Javascript challenge problems when I came across one that threw me for a bit of a — wait for it — loop

Yeah, that was bad. Okay, so let’s take a quick look at the problem.

// What will the following code output?const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log('Index: ' + i + ', element: '+ arr[i]);
}, 3000)
}

Go on, give it a shot! No cheating!

Alright! Ready for the answer?

Index: 4, element: undefined
Index: 4, element: undefined
Index: 4, element: undefined
Index: 4, element: undefined

If you nailed it, great job! You probably have a strong grasp of the concepts we’re going to go over here! Move along, now. Seriously, get out.

Ahem. Now that we’ve separated the stayers from the goers, let’s take a closer look at the setTimeout function. setTimeout is a higher order function (a function that takes one or more callback functions as parameters). It has two arguments: the first argument is the function to be invoked, and the second argument is a time interval in milliseconds. setTimeout’s job, when called, is to immediately set a timer that will expire after a specified time interval (the second argument to setTimeout). When that timer expires, the code that is in the callback function of the first argument passed to setTimeout is executed.

As some of you may have guessed, one of the key concepts of this problem is that setTimeout is creating a closure. So what’s a closure? A closure is an inner function that has access to the outer function’s variables — scope chain. The closure has three scope chains: it has access to its own scope, the outer function’s variables, and global variables. The inner function also has access to the outer function’s parameters. In our case, the outer scope that our inner function has access to is the loop that contains the index i.

After 3 seconds go by, the function is executed and it prints out the value of i, which at the end of the loop is at 4 because it cycles through 0, 1, 2, 3, 4 and the loop finally stops at 4. setTimeout does not wait for the time interval to expire and then execute, it executes immediately. It is the callback function in setTimeout’s first argument that waits/executes. arr[4] does not exist, which is why you get undefined.

So how do we solve this bad boy? There’s a few ways we can do it! How about passing each i value in as an argument to the callback function?

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
setTimeout(function(now_memory) {
return function() {
console.log('Index: ' + now_memory + ', element: '+
arr[now_memory])
}
}(i), 3000)
}

That should do it!

Index: 0, element: 10
Index: 1, element: 12
Index: 2, element: 15
Index: 3, element: 21

But wait! ES6 syntax can make this problem even easier — using let! let works similarly to var, but the variable it declares is block-scoped (it only exists within the current block), while var is function-scoped.

const arr = [10, 12, 15, 21];
for (let i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log('Index: ' + i + ', element: '+ arr[i]);
}, 3000)
}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store