-1

I am having a hard time understanding why let works in async for loops in JavaScript.

I am using an asynchronous function to do various insertion and retrieval from a database

My code looked something like this:

for (var i = 0; i < row.length; i++) {
   pool.query(`Select Name from Students where ID = ${row[i].PartID}`)

                .then(rows => {
                    ....
                    ....

}

The array was starting from the max i value instead of 0 in the row[i]. I understood what I was doing wrong but when I was looking for a solution to this I found that using let instead of var i fixes the problem.

I however do not understand why this is the case. Why does using let stop the variable i from increasing before the async function execution?

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
jedu
  • 1,211
  • 2
  • 25
  • 57
  • Following myself but i don't think using `let` or `var` for async would have any difference. – Alwaysblue Jul 24 '19 at 18:55
  • @esqew I didn't got the question correctly but as per whatever understanding I have for the question, is there any difference for using `let ` or `var` for **async**? – Alwaysblue Jul 24 '19 at 18:58
  • 1
    @iRohitBhatia in one case, each iteration gets it's own variable, in the other the same variable is used for each iteration. The latter results in the same value being logged n times (assuming you use the variable in the callback) – Kevin B Jul 24 '19 at 18:58
  • See also [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/a/750506/215552). – Heretic Monkey Jul 24 '19 at 18:58
  • If the variable is never used in the callback, which one you use is irrelevant. – Kevin B Jul 24 '19 at 18:59

2 Answers2

6

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.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Anyone care to offer what problem in this answer led to a downvote so I can improve it? – jfriend00 Jul 24 '19 at 19:13
  • Thank you this answers my question. Not sure why this was downvoted. Marking this as the top answer. – jedu Jul 24 '19 at 19:40
-1

One thing I would look at is whether or not each iteration of the for loop is waiting for your async function to resolve. It might just keep going during the time it takes for you to connect to the database and perform the operation. That's why it was starting with max.

Other than that, it isn't clear why using 'let' would matter even with the new block-scoping rules shown here.

Alex Otten
  • 9
  • 1
  • 3