1

I have many arrays of values being sent from my view to the controller. Example:

year: ['2015','2016','2017']
cash: ['...','...','...']
.
.
.

0th index of each of these array forms one record in cashflow database table. 1st record another, and so on...cashflow is a collection inside "Property" model. I need to create these 'cashflows' iteratively whenever a record for 'property' is created.

I'm using sails framework. I am not able to create these records iteratively and render it all at once. I'm not sure if this needs to be done using async tasks.

Property.create(reqParams).exec(function(err, property) {
  if (err) {
    ...
  } else {
    var index = 0;
    var asyncTasks = [];

    for (index = 0; index < reqParams.year.length; index++) {

      asyncTasks.push(function(callback) {

          CashFlowProjections.create({
              year: reqParams.year[index],
              belongsTo: property.id,
              cash: reqParams.cash[index],
              ...)
            .exec(function(err, cashflows) {
              callback(err, cashflows);
            });
          });
      }

      async.parallel(asyncTasks, function(err, results) {
        if (err) {
          res.handleError(0, err);
        } else {
          res.view('property/preview', {
            cashflows: results[0],
            ...
          });
        }
      });
    }
  }
});

But the above code is wrong since the 'index' ends up being the same value for all the records and record creation fails.

I have also tried it without async tasks but I faced a problem of "res.view" getting executed before the creation of the cashflows records. Can someone guide me with this?

A.R.K.S
  • 1,692
  • 5
  • 18
  • 40

1 Answers1

3

The problem is with the scope of the variable index. index is scoped to the function that begins on line 1. asyncTasks becomes an array of functions which are closures - they share the variable with their enclosing scope. Since these functions are called asynchronously, after the for loop has finished, index has a value of reqParams.year.length by the time they are called.

To get round this, you could create a separate scope for each function, and in these scopes make a copy of index, which will not be affected by the index++ of the original index. Like this:

for (index = 0; index < reqParams.year.length; index++) {

  (function(index) {
    asyncTasks.push(function(callback) {

      CashFlowProjections.create({
          year: reqParams.year[index],
          belongsTo: property.id,
          cash: reqParams.cash[index],
          ...})
        .exec(function(err, cashflows) {
          callback(err, cashflows);
      });
    });
  })(index);
}

If you do this, there are really two separate variables called index:

  • the original one, declared on line 5 of your original script and used on the for line and also in })(index);. Its value starts as 0 and is incremented each time it goes through the loop.
  • a new, inner one, declared at function(index). This overrides the original one, but only inside the function definition. In fact this is really several separate scopes: every time it goes through the loop, it creates a new (anonymous) function and calls it, and calling the function creates the scope. Its value is set to whatever the outer index was at the time the loop was run, is never changed after that, and is used when async.parallel runs the task.

So if you prefer you can rename one of the two variables to avoid confusion.

David Knipe
  • 3,417
  • 1
  • 19
  • 19
  • Thanks for the clear explanation, @David. It worked!! I just found out this has been discussed on other post. Couldn't find it earlier since this post topic names the solution instead of the problem. :) Posting link here if it helps others like me http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example – A.R.K.S Jul 28 '15 at 22:44