2

I want to import some data from a json file (which contains a json array of objects) into mongodb using mongoose. When the route '/import' is called I load the file via "require" and start iterating.

router.get('/import', function (req, res) {
  var data = require('../import/1.json');
  nTotal = data.length;
  for (var i = 0; i < nAppCount; i++) {
    var obj = new MyObject(data[i]);
    obj.save(check);
  }
});

Since JSHint warns me not to "make functions inside a loop", I created my function called check as follows:

function check (err,doc){
  nProcessed++;
  if (err) {
    logger.log('error', err.message, err.errors);
  } else {
    logger.log('debug', "%d of %d processed",nProcessed, nTotal);
    if (nProcessed===nTotal) {
      res.render('index');
    }
  }
}

Everything works as expected until the end but when all objects are processed I get:

ReferenceError: res is not defined

Isn't "check" supposed to "know" res variable being a closure function? How am I going to access "res"?

Note: I can make it work if I ignore jsHint warning.

salihcenap
  • 1,927
  • 22
  • 25
  • Put the declaration of `check()` at the top of that callback function you're passing to `router.get()`. – Pointy Oct 16 '15 at 18:17
  • 2
    Indeed, you have to be in the same (functional) scope in order to be able to capture the closure. See [How do JavaScript closures work?](http://stackoverflow.com/questions/111102/how-do-javascript-closures-work). – leesei Oct 16 '15 at 18:20
  • is `nAppCount` supposed to be `nTotal`? if so, the right way to do this would be with a forEach loop if `data` is an array, thus avoiding this problem entirely. I tend to avoid using for loops all together in favor of functional methods that create their own scope to avoid any var leakage. – Kevin B Oct 16 '15 at 18:31
  • Where is the function `check` actually defined? – thefourtheye Oct 16 '15 at 18:32
  • Yes "check" was defined outside! That was the mistake. – salihcenap Oct 16 '15 at 18:35

1 Answers1

4

The main thing to understand here is, the variables are enclosed during the creation of the function not during the invocation of the function.

In your case, when the function check is created, it doesn't enclose res or req. So, even when you invoke it from other functions, it will not be aware of them.

To fix this, you can simply define the check function within the router.get and before the for loop. For example,

router.get('/import', function (req, res) {
  ...

  function check(err, doc) {
    ...
  }

  for (var i = 0; i < nAppCount; i++) {
    (new MyObject(data[i])).save(check);
  }

});
thefourtheye
  • 233,700
  • 52
  • 457
  • 497