1

I have just started programming with JS and Node, and I haven't got used to asynchronous stuff yet. Basically, i have the following code:

for (var i=0, len=sources.length; i<len; i++) {
    processSource(sources[i], function(info) {
        doesOtherStuff(sources[i], info);
    });
}

It doesn't really work, because, as processSource takes a while to finish, the function doesOtherStuff is called with unmatching arguments, like sources[2] and the processed info for sources[0].

What is the correct way of dealing with this? Is there something inherently wrong with the design of these functions. (both processSource and doesOtherStuff are my functions).

Márcio Paiva
  • 983
  • 9
  • 25

4 Answers4

2

The problem with the code is the fact that i is not want you expect it to be.

When the loop completes, the function-level variable i has the value of sources.length. So when doesOtherStuff runs that is what the inner function uses.

for (var i=0, len=sources.length; i<len; i++) {
    (function(i) {
        processSource(sources[i], function(info) {
            doesOtherStuff(sources[i], info);
        });
    })(i);
}
epascarello
  • 204,599
  • 20
  • 195
  • 236
  • So this is actually standard, is is it more like a hack to code that was bad-written in the first place? I'm still trying to get my head around event-driven. – Márcio Paiva Dec 12 '13 at 17:57
  • 1
    That pattern is commonly called an IIFE (immediately-invoked function expression) in case you're looking for more information about it. – Aaron Dufour Dec 12 '13 at 18:10
  • Love this, also we can do +function() {...}(i); :) – damphat Dec 12 '13 at 19:05
1

javascript style could help you:

sources.forEach(function (e) {
    processSource(e, function(info) {
        doesOtherStuff(e, info);
    });
}
damphat
  • 18,246
  • 8
  • 45
  • 59
0

1) Use var instead of int.

2) You have a superfluous ) in your call to processSource.

processSource(sources[i], function(info) /* No 2nd ')' here */ {
    doesOtherStuff(sources[i], info);
});

should work.

Max Truxa
  • 3,308
  • 25
  • 38
0

Try using Caolan's async library - this works on Node and in the browser. You can then do something like this:

async.map(sources, function (item, callback) {
    // Do your actions on each item here
    processSource(item, function (info) {
        var result = doOtherStuff(item, info);

        // Send callback so the next item can be processed
        callback(null, result);
    });
}, function (err, results) {
   // Handle the processed results here
});
Clarence Leung
  • 2,446
  • 21
  • 24