var
has function scope.
let
has block scope.
So, in a for
loop like this:
for (let i = 0; i < row.length; i++)
Each iteration of the loop has it's own separate i
variable. When using asynchronous operations inside the loop, that keeps each separate i
variable with it's own asynchronous callback and thus one doesn't overwrite the other.
Since var
is function scoped, when you do this:
for (var i = 0; i < row.length; i++)
There's only one i
variable in the whole function and each iteration of the for
loop uses the same one. The second iteration of the for
loop has changed the value of i
that any asynchronous operations in the first iteration may be trying to use. This can cause problems for those asynchronous operations if they are trying to use their value of i
inside an asynchronous callback because meanwhile the for
loop will have already changed it.
Here's a classic example:
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 10);
}
And, now with let
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 10);
}
Run each one and see the difference in results. In both examples, the for
loop runs to completion and schedules 5 timers. In the var
example, all the timer callbacks see the one i
variable from the now finished for
loop and thus they all output 5
as the value of i
.
In the let
example, each iteration of the loop has its own i
variable that is unaffected by the march of the for
loop so when their timer callback is called (after the for
loop has finished), they still have the same value of i
that they had when their iteration of the for
loop was originally executed and thus they output the expected sequence of increasing numbers.