2
var data = [{offset: 2000, str:'foo'}, {offset: 4000, str:'bar'}];

for (var i=0; i<data.length; i++) {
    var x = data[i];
    setTimeout(function(){printStuff(x.str)}, x.offset);
}

function printStuff(str) {
    console.log(str);
}

I was expecting to get printStuff('foo') at 2000 ms offset and printStuff('bar') at 4000 ms offset but instead it prints 'bar' both times. I've no idea what's going on, help please.

1
  • it's a scoping issue; when your printStuff finally runs, x has been reassigned to {offset:4000,str:'bar'} Commented Jul 6, 2012 at 14:22

3 Answers 3

3

Do this :

for (var i = 0; i < data.length; i++) {
    (function (x) {
        setTimeout(function () {
            printStuff(x.str)
        }, x.offset);
    })(data[i]);
}

Your problem is that x has changed in the closure when the function is called.

2

You can use functional iteration that gives a closure for free:

data.forEach( function( x ) {
      setTimeout( printStuff.bind(null, x.str), x.offset );
});

Shims for all in case oldIE support is required:

forEach

bind

2
  • I didn't knew this new and interesting function. Do you have precision about the compatibility (including mobiles) ? Commented Jul 6, 2012 at 14:34
  • @dystroy the compatibility and user-made code to make those functions work in unsupported browsers can be found in the links I gave. Roughly any browser made/patched after december 2009 should support them.
    – Esailija
    Commented Jul 6, 2012 at 14:35
0

setTimeout does not block the code from continuing to execute, so the loop completes, and by the time the setTimeout callback is executed, the latest value for 'x' is the second object in the data array.

Not the answer you're looking for? Browse other questions tagged or ask your own question.