Nodejs and mongodb (mongojs): trying to query and update a database in a for loop

I am writing a multiplayer game (mongojs, nodejs) and am trying to figure out how to update user stats based on game results. I already have some code written to calculate all stats after the game. The problem arises when I try to update user statistics in a for loop. This is what I got:

//Game Stats
var tempgame = {
    gameid: 1234,
    stats: [
        {
            score: 25,
            user: 'user1'
        },
        {
            score: 25,
            user: 'user2'
        }
    ]
}


for(i = 0; i < tempgame.stats.length; i++){
    db.users.find({ username: tempgame.stats[i].user }, function(err, res){
        if( err != null){
            //handle errors here.
        } else {
            var userstats = res[0].stats;
            if( tempgame.stats[i].score > userstats.bestscore ){ //this is where it chokes 
                userstats.bestscore = tempgame.stats[i].score;
            }

            //code here to pass back new manipulated stats
        }
    });
}

      

Everything works fine until I try to use the tempirame object in the callback function. It says "Unable to read the 'score' of property undefined. Is this just a problem?

Also I thought it might be a problem with the callback function itself. Perhaps the loop will grow longer before the callback is fired. But even so the score should be there, it will just pull from the wrong array index ... which made me believe it might be a sphere problem.

Any help would be greatly appreciated.

+3


source to share


3 answers


You are facing the notorious problem of defining functions within a loop .

Use "forEach" instead:



tempgame.stats.forEach(function (stat) {
    db.users.find({ username: stat.user }, function(err, res){
        if( err != null){
            //handle errors here.
        } else {
            var userstats = res[0].stats;
            if( stat.score > userstats.bestscore ){ //this is where it chokes 
                userstats.bestscore = stat.score;
            }

            //code here to pass back new manipulated stats
        }
    });
});

      

+7


source


Part of your problem is that mjhm is quoted in his answer to your question and as you suspected. The variable is i

changed before the callback is called.

The other half of the problem has to do with the fact that your calls to the database haven't returned yet. Due to the asynchronous nature of NodeJS, your loop will complete before your calls are complete. Also, your calls to the database do not necessarily return in the same order that you gave them. You need some kind of flow control like async.js . Usage async.map

will allow you to make all calls to the db in parallel and return them as an array of values ​​that you can use after all db calls have completed.



async.map(tempgame.stats, function(stat, callback){
  db.users.find({ username: stat.user }, function(err, res){
      if( err != null){
          callback(err);
      } else {
          callback(null, res[0].stats);
      }
  });      
}, function(err, stats){
  if(err){
    //handle errors
  } else{
    stats.forEach(function(stat){
      //do something with your array of stats
      //this wont be called until all database calls have been completed
    });
  }
});

      

+2


source


In addition to the above, if you want to feed the results back to the application, http://nodeblog.tumblr.com/post/60922749945/nodejs-async-db-query-inside-for-loop

0


source







All Articles