3

Folks, I have the following for loop, inside of which I call vehicles.alertsVid function from another module, which returns a status for each vehicle. The results do arrive, but later, and do not get added properly.

What is the proper way to write this, so the main for loop does not race ahead, and temp.status gets written in time? :)

for (var i = vehiclesStuff.length - 1; i >= 0; i--) {
var temp = {};
temp.name = vehiclesStuff[i].nickname.S;
temp.plate = vehiclesStuff[i].plate.S;
temp.base = vehiclesStuff[i].base.S;
temp.vin = vehiclesStuff[i].vin.S;
temp.vid = vehiclesStuff[i].vid.S;

var myfunc = function(t,results,done){return function(){
    console.log ("ALERTS",results);
    t.status = results.toString();
    done();
}};

vehicles.alertsVid(temp.vid, myfunc(temp));

vehicleArray.push(temp);
};
callback()
Cmag
  • 14,946
  • 25
  • 89
  • 140
  • 1
    You miss all semicolons, and put one where it doesn't belong? – elclanrs Jan 19 '14 at 23:50
  • @elclanrs - Ah, a chance to debate semicolons. They're not there, but I don't actually _miss_ them... (No, I don't really want to debate it.) – nnnnnn Jan 19 '14 at 23:51
  • Although the code makes it look that way, temp is not loop scoped due to hoisting, so if the function passed to alertsVid actually gets called later, it will be called with the final value of temp for all versions of the loop rather than stepping through the possibilities. Is that what is happening? – kybernetikos Jan 19 '14 at 23:51
  • @nnnnnn: I'm aware of ASI, this is more question to OP why is that semicolon there, makes no sense even in semicolonless JS. – elclanrs Jan 19 '14 at 23:52
  • 2
    _"so the main for loop does not race ahead"_ - Note that this isn't a "race" situation. Your JS will be single-threaded, but presumably `vehicles.alertsVid()` triggers an asynchronous process so the function you pass to it won't be called until after the entire for loop has finished and after `callback()` returns. @elclanrs - Yes, I know. I was just being silly (if you can imagine). – nnnnnn Jan 19 '14 at 23:53
  • Ditto, Quentin is right. This can be solved using closures. – Arjun Mehta Jan 20 '14 at 01:36
  • @ArjunMehta not sure how to use the example you guys gave... – Cmag Jan 20 '14 at 01:40
  • 1
    Okay, I hope my second answer helps. – Arjun Mehta Jan 20 '14 at 02:37

3 Answers3

2
for (var i = vehiclesStuff.length - 1; i >= 0; i--) {
    var temp = {}
    temp.name = vehiclesStuff[i].nickname.S;
    temp.plate = vehiclesStuff[i].plate.S;

    var myfunc=function(t,results,done){return function(){
        console.log ("ALERTS",results);
        t.status = results.toString();
        done();
    }};


    vehicles.alertsVid(temp.vid, myfunc(temp,results, done));

    vehicleArray.push(temp);
};

When you call the internal function, the variables are bound to the call stack AT THE MOMENT OF CALLING. Therefore the callback function will not "race ahead".

Note that the function returns a function itself, and hence the double nesting. One of my signatures. =)

Schien
  • 3,855
  • 1
  • 16
  • 29
  • 2
    I don't think the problem is with results/done, but rather with temp. In fact, I rather suspect that the callback is supposed to receive results/done as arguments, so your code as it stands is probably the wrong way round, and the returned function should still have results/done as arguments, but the outer function should have temp as an argument. – kybernetikos Jan 20 '14 at 00:01
  • I'm passing the temp variable as well now, in case of out-of-scope at evaluation time. Thanks for pointing that out. I ignored what was inside the function and only changed the call flow. – Schien Jan 20 '14 at 00:06
  • @Schien: Regardless, you're still passing `undefined` for `results` instead of getting them from the callback parameter. – Bergi Jan 20 '14 at 00:07
  • What we're saying is that `function(t,results,done){return function(){` should be `function(t){return function(results,done){` – kybernetikos Jan 20 '14 at 00:11
  • Folks, similar behavior if i replaced my code with your suggestions: http://pastie.org/8649335 – Cmag Jan 20 '14 at 01:06
  • Also, I get `results` undefined if I use `vehicles.alertsVid(temp.vid, myfunc(temp,results, done));` – Cmag Jan 20 '14 at 01:08
  • i've updated my code, any suggestions? – Cmag Jan 20 '14 at 01:13
  • 1
    This method too also requires that you're able to modify vehicles.alertsVid to take in and return the whole temp object and return it as t. It will not work if you do not modify vehicles.alertsVid to pass through the temp object. – Arjun Mehta Jan 20 '14 at 01:30
  • That's right. The signature for vehicles.alertsVid has to be changed to take the callback function as a variable, and invoke it later. – Schien Jan 20 '14 at 01:36
  • Folks, still no-go: https://gist.github.com/vasiliyb/8527211 – Cmag Jan 20 '14 at 19:21
2

You should familiarize yourself with the concept of closures. Closures are a powerful way of capturing the context of any given evaluation/execution chain.

function callbackClosure(temp){
  return function(results, done){
    console.log ("ALERTS",results);
    temp.status = results.toString();
    done();
  };
}

for (var i = vehiclesStuff.length - 1; i >= 0; i--) {
  var temp = {};
  temp.name = vehiclesStuff[i].nickname.S;
  temp.plate = vehiclesStuff[i].plate.S;
  temp.base = vehiclesStuff[i].base.S;
  temp.vin = vehiclesStuff[i].vin.S;
  temp.vid = vehiclesStuff[i].vid.S;

  vehicles.alertsVid(temp.vid, callbackClosure(temp));

  vehicleArray.push(temp);
}

What this code is doing is it is creating a new context by returning a function that is scoped by the callbackClosure execution instance with a particular passed in object that will be kept in its unique scope until execution.

This is assuming that your vehicles.alertsVid executes and returns in its callBack a results object, as well as a done function.

Learn more about closures at this question: How do JavaScript closures work?

Community
  • 1
  • 1
Arjun Mehta
  • 2,500
  • 1
  • 24
  • 40
  • Unfortunately, following your `callbackClosure(temp)` still does not work... Here is a Gist: https://gist.github.com/vasiliyb/8527211 – Cmag Jan 20 '14 at 19:20
  • I think you're not quite grasping the concept of asynchronous programming from your gist, let alone javascript. Also, you are still not using semicolons properly at all. :) In your gist, try console.log(temp) from within the function returned by the callbackClosure function. And here, watch these videos: http://yuiblog.com/crockford/ Aside from this, I've helped as much as I can. See if you can make it work! ;) – Arjun Mehta Jan 20 '14 at 19:59
1

If you have control over the alertsVid function that is called from vehicles you may like to pass it the whole temp object and let it handle that instead of temp.vid (where does this property come from anyway?).

This way, you are passing the same object reference so that when it is finally processed, you are adding the status to the right temp (rightTemp). That way it can return the right temp as an argument in the callBack.

You are initializing a new object temp, and adding name and plate. I don't see vid as a property on temp?

Also if you want to wait until all of your temps are processed and with a status BEFORE you call your final callback you can use a counter to wait for it to fire. If you don't want to do this, just ignore those lines... (I've commented with "// callBack handling")

Also... fix those semicolons. :P

var numProcessed = 0; // callBack handling
var numToProcess = vehiclesStuff.length; // callBack handling

for (var i = vehiclesStuff.length - 1; i >= 0; i--) {
    var temp = {};
    temp.name = vehiclesStuff[i].nickname.S;
    temp.plate = vehiclesStuff[i].plate.S;

    vehicles.alertsVid(temp, function (rightTemp, results, done) {
        console.log ("ALERTS",results);
        rightTemp.status = results.toString();            
        done();
        processedTemp(); // callBack handling
    })

    vehicleArray.push(temp);
}

// callBack handling
function processedTemp(){
  numProcessed++;
  if(numProcessed === numToProcess) callBack();
}

your vehicles.alertsVid function would then look like:

vehicles.alertsVid = function(temp, callBack){
  ...
  callBack(temp, results, done); // using the same temp (object reference) that was passed in!!!
};

This is all assuming that you have control over the vehicles.alertsVid function.

Arjun Mehta
  • 2,500
  • 1
  • 24
  • 40