-1

I know that this problem is related to JS scope and I have searched, but can't get the solutions from other stackoverflow questions to work.

I have this program

http://jsfiddle.net/0z525bhf/

function write(x, y) {
    console.log(x);
    console.log(y);
}

var data = {
    "property": {
        "1": {
            "values": [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
        },
        "2": {
            "values": [[11, 12], [13, 14], [15, 16], [17, 18], [19, 20]]
        }
    }
}

var delay = 1000;

for (var x in data.property) {
    for (var i = 0; i < data.property[x].values.length; i++) {
        var one  = data.property[x].values[i][0];
        var two  = data.property[x].values[i][1];

        setTimeout(function() {
            write(one, two);
        }, delay);

        delay += 1000;
    }
}

that reads data from an object and loops over both the object and the arrays in it. I want it to print the array values one second apart, but it always prints the values from the last iteration. I have tried with closures as suggested in the other question, but can't get it to work.

Community
  • 1
  • 1

3 Answers3

1

You could have applied you own research and used the following method.

for (var i = 0; i < data.property[x].values.length; i++) {
    (function (one, two) {
        setTimeout(function() {
            write(one, two);
        }, delay);
    })(data.property[x].values[i][0], data.property[x].values[i][1]);

    delay += 1000;
}

Working jsfiddle: http://jsfiddle.net/0z525bhf/1/

Community
  • 1
  • 1
sWW
  • 585
  • 4
  • 13
1

In the specific case of setTimeout, you don't even need a closure. You can pass variables to the timeout function, as follows:

for (var x in data.property) {
    for (var i = 0; i < data.property[x].values.length; i++) {
        var one  = data.property[x].values[i][0];
        var two  = data.property[x].values[i][1];

        setTimeout(function(one, two) {
            write(one, two);
        }, delay, one, two);

        delay += 1000;
    }
}
Scimonster
  • 32,893
  • 9
  • 77
  • 89
0

That's a problem with lexical closures : the function you pass to setTimeout closes over the variables one and two which get modified during iteration. This is why you should never declare a function in the body of a for loop.

Using a functional loop will solve it :

var delay = 1000;
for (var x in data.property) {
  data.property.value[x].forEach(function (tuple) {
    var one = tuple[0], two = tuple[1];
    setTimeout(function() {
      write(one, two);
    }, delay);
    delay += 1000;
  });
}
Valentin Waeselynck
  • 5,950
  • 26
  • 43
  • Thank you for the explanation. I had to edit it in one place, here's the working version for the provided example: http://jsfiddle.net/qdstdyrh/ – user1111111111 Aug 12 '14 at 11:55