Understanding JavaScript's var vs. let in Asynchronous Code

Understanding JavaScript's var vs. let in Asynchronous Code
Photo by Aleks Dorohovich / Unsplash

When working with JavaScript, one of the most common challenges for beginners is understanding how var and let behave in different situations, especially when dealing with asynchronous code like the setTimeout function. Let's explore this concept by analyzing two similar code blocks that produce different outputs.

Imagine you want to loop over a set of numbers and log each value after a slight delay. You might use a for loop with setTimeout like this:

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1);
}

At first glance, you might expect this to log 0, 1, and 2. However, when you run the code, it logs 3 three times. This can be confusing, but the reason lies in how var works in JavaScript.

Why Does It Log 3?

The var keyword is function-scoped, meaning it does not create a new binding for each iteration of the loop. Instead, var i refers to the same variable throughout the loop, and its value is updated at each iteration. By the time the setTimeout callbacks run, the loop has already finished, and i has become 3. This is why all three console.log(i) calls print 3—because the loop has completed and i remains 3.

Here’s what happens step by step:

  1. The loop iterates with i = 0, i = 1, and i = 2.
  2. Each time, setTimeout schedules a function to run later.
  3. After the loop ends, i is now 3.
  4. When the setTimeout callbacks run, they all refer to this final value of i.

Now, compare this with a slightly different version of the same loop:

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1);
}

This time, the output is exactly what you'd expect: 0, 1, and 2. The reason for this difference is the use of let instead of var.

Why Does let Work Differently?

Unlike var, let is block-scoped, meaning it creates a new binding for i in each iteration of the loop. Each iteration gets its own copy of i, so when the setTimeout callback is executed, it captures the value of i as it was during that iteration. As a result, the values 0, 1, and 2 are printed in sequence, as expected.

This distinction between function-scoping and block-scoping is crucial for writing predictable asynchronous JavaScript code. By understanding how let and var behave in loops, you can avoid subtle bugs and ensure that your callbacks reference the correct values.

Important to Remember:

  • var is function-scoped, meaning that all iterations of the loop share the same variable. In asynchronous code, this can lead to unexpected results because the variable might change before the callback runs.
  • let is block-scoped, meaning that each iteration of the loop has its own instance of the variable, ensuring that asynchronous callbacks refer to the correct value.

In summary, when writing loops that involve asynchronous operations like setTimeout, it's often better to use let to ensure the behavior you expect. This small change can make a big difference in how your code behaves, especially when dealing with delayed or asynchronous actions.

Support Us

Subscribe to Buka Corner

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Subscribe