-3

Given this:

var m = 5;

for (i = 0; i < m; i++) { 
    add_row('row-'+(i+1), () => f(i))
}

if I do an alert in my f the it will always output the value 5. I believe this is due to the same problem mention here for python:

lambda function don't closure the parameter in Python?

How is this problem solved in javascript?

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
Baz
  • 12,713
  • 38
  • 145
  • 268
  • 1
    `python` != `javascript`. Also I don't see your `f`. Can you please include a brief example of its contents? – Shadow Nov 16 '17 at 22:55
  • 1
    @Shadow Its contents are irrelevant. The value of `i` is the whole issue. – Barmar Nov 16 '17 at 23:06

1 Answers1

1

This is because add_row() is asynchronous. It calls the callback you pass it sometime LATER when the operation finishes. Meanwhile, the for loop has already finished. One simple solution in ES6, is to use let with your for loop. This creates a new and separate i variable for each run of the loop so when the callback is called sometime LATER, the variable i for that invocation of the loop is still at it was when the function was started.

If you were to insert some console.log() statements into your code like this:

var m = 3;

console.log("A");
for (i = 0; i < m; i++) { 
    console.log("B" + i);
    add_row('row-'+(i+1), () => {
        console.log("C" + i);
        f(i);
    })
}
console.log("D");

What you would see in the console is this:

 A
 B0
 B1
 B2
 D
 C0
 C1
 C2

Notice how "D" comes before any of the "Cx" lines. That shows you how the asynchronous callback is called LATER after your for loop is done executing.

Please study that and when you fully understand the reasoning for that order, then you will finally understand an asynchronous callback. Technically, the C0, C1 and C2 will be at the end, but could be in any order relative to each other.

Here's how you can use let for the for loop to create a separate variable i for each iteration of the loop so you will still have the appropriate value of i when the callback is called some time later:

for (let i = 0; i < m; i++) { 
    add_row('row-'+(i+1), () => f(i))
}

Before ES6, one could fix this by creating an extra closure which creates a new function scoped variable to "remember" the loop index separately for each invocation:

for (var i = 0; i < m; i++) { 
    (function(index) {
        add_row('row-'+(index+1), () => f(index))
    })(i);
}
jfriend00
  • 683,504
  • 96
  • 985
  • 979