Frage

I do have an issue with scoping variables in Javascript (Node.js / Express)

Can someone please explain to me why is the "out" variable not being scoped to the last line in the following script?

exports.last = function(req, res){
  var out = [];
  var q = Point.find({ token: req.params.token }).distinct('key');
  q.exec(function (err, keys, count) {
    keys.forEach(function(k) {
      var q2 = Point.findOne({ token: req.params.token, key: k }).sort({time:-1})
      q2.exec(function(err, p, count) {
        out.push({ a: Date.parse(p.time), b: parseFloat(p.val) });
      });
    })
    console.log(out);
  })
  res.json(out);
};
War es hilfreich?

Lösung

The commentors are correct: this is not a scoping issue, but a consequence of the asynchronous nature of JavaScript. Imagine you could assign an "step number" to every line of code. Here is a simplified view of your code (the numbers are step numbers, as the JavaScript engine executes things, NOT line numbers):

1   var thing = [];
2   q2.exec(function(){
        // some stuff happens
15      thing.push('hello!');
    });
3   console.log(thing);  // still empty!

See how the flow goes 1, 2, 3, skipping over the function call in q2.exec? That's because q2.exec is asynchronous: you're saying "whenever q2.exec is done, call this function." You don't know when it's going to be done. So it proceeds on to step 3, and then it does other stuff...eventually, q.exec finishes, and it executes your function (in this fictional example, it's step 15).

Hope that clarifies things.

As far as how to fix it, you'll have to either employ callbacks or, more likely in this case, use "promises". Why promises in this case? Because you're calling q2.exec a bunch of times, and I assume you only want to log to the console after all the q2.exec calls have completed. So you could do something like this (using the Q promises library):

var Q = require('q');  // this has nothing to do with your 'q2'
var promises = [];
var thing = [];
stuff.forEach(function(x,i){
    var deferred = Q.defer();
    q2.exec(function(){
        thing.push('hello ' + i);
        deferred.resolve();
    });
    promises.push(deferred.promise);
});
Q.all(promises).then(function(){
    // all calls to q2.exec now complete
    console.log(thing);
});
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top