Today I Learned - Rocky Kev

TIL setTimeout, blocks, closure

POSTED ON:

TAGS:

This is a fantastic question.


// What will the following 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)
}

There's some interesting things in here:

  1. There's closures
  2. There's a callback
  3. There's an anonymous function setTimeout's callback.
  4. We're using ES6 stuff (that const)
  5. How setTimeout uses the stack queue.

The result:

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

Step-by-step of what's happening:

  1. Look at the var i = 0 element.

setTimeout is creating a closure function.

a JavaScript closure is when an inner function has access to its outer enclosing function’s variables and properties

  1. The closure has access to i, which it will take.

  2. It's creating 4 anonymous functions, each one firing after 3000 milliseconds.

So....

loop 0 (i == 0) - create function.
loop 1 (i == 1) - create function
loop 2 (i == 2) - create function
loop 3 (i == 3) - create function
loop 4 (i == 4) - stop

  1. The anonymous function is executed AFTER 3000 milliseconds is up.

But wait, remember step 2?

Closure is taking the i variable.
What is the i variable? Well, according to step 3, i == 4!

console.log('Index: ' + i + ', element: ' + arr[i]);

OR

console.log('Index: ' + 4 + ', element: ' + arr[4]);

**The result: **

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

How to Fix it

If you swap the var for a let, it'll work correctly.
Because let will create a block scope.


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

// looks the same as

for (var i = 0; i < arr.length; i++) {

( function() {
var internal_i = i; // the let creates it's own block scope, which then stores i internally
setTimeout(function() {
console.log('Index: ' + internal_i + ', element: ' + arr[internal_i]);
}, 3000)
}
)
}
Index: 0, element: 10
Index: 1, element: 12
Index: 2, element: 15
Index: 3, element: 21

REF:

A Common JavaScript Interview Question Asked By Google & Amazon
why let and var behave differently - Stack overflow


Related TILs

Tagged:

TIL setTimeout, blocks, closure

setTimeout is creating a closure function. More importantly, vars get yoinked by closure, where lets create their own block scope.

TIL setTimeout, blocks, closure

setTimeout is creating a closure function. More importantly, vars get yoinked by closure, where lets create their own block scope.

TIL setTimeout, blocks, closure

setTimeout is creating a closure function. More importantly, vars get yoinked by closure, where lets create their own block scope.