triscuit312 triscuit312 - 5 months ago 16
Node.js Question

How to set up and access class properties in Node.js?

I'm having trouble acessing the data in objects I've created. I'm very new to JS and node, and I think my issue is how I'm initializing the variables, but I don't know.

Here's my initializations:

var http = require('http');
var MongoClient = require('mongodb').MongoClient;
var async = require('async');
var currentBoatList = [];
var BoatObjectList = [];


I have a class to create a boat's current info (taken from a database):

function CurrentBoatInfo(boatName) {
var name,MMSI,callSign,currentDate,positionJSON,status,speed,course;
database.collection('Vessels').find({"BoatName":boatName},{"sort":{DateTime:-1}}).toArray(function(error1,vessel) {
name = vessel[0].BoatName;
MMSI = vessel[0].MMSI;
callSign = vessel[0].VesselCallSign;
console.log(name); \\logs the boats name, so the variable is there
});
});
}


I have my db function that pulls recent boats, puts their names in a list, and then in another list creates objects for each boatname in the list:

EDIT: I see I'm unnecessarily connecting to the mongoDB multiple times, working the code to fix that, and clear up 'db' variable name.

var createBoats = function() {
MongoClient.connect('mongodb://localhost:27017/tracks', function(err,database){
if (err) {return console.dir(err); }
else {console.log("connect to db");}
database.collection('Vessels').find({"MostRecentContact": { "$gte": (new Date((new Date()).getTime() - (365*24*60*60*1000)))}}).toArray(function(error,docs) { //within a year
docs.forEach(function(entry, index, array) {
currentBoatList.push(entry.BoatName); //create list of boats
BoatObjectList.push(new CurrentBoatInfo(entry.BoatName,database));
});
server();
});
});
};


and finally my server code that simply creates a server, and is supposed to log some information from each of the objects created above, but for some reason doesn't (output below):

var server = function() {
http.createServer(function handler(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
console.log(BoatObjectList); //array of CurrentBoatInfo objects, prints [CurrentBoatInfo {}, CurrentBoatInfo {}, CurrentBoatInfo {}]
console.log(BoatObjectList[0].name); //prints undefined
BoatObjectList.forEach(function(entry) {
var count = 0;
for(var propertyName in entry.CurrentBoatInfo) { //nothing from here prints
console.log(JSON.stringify(propertyName));
count++;
console.log(count);
}
});
res.end();
}).listen(1337, '127.0.0.1');
};


The output I see is something like:

connect to db
[ 'DOCK HOLIDAY', 'BOATY MCBOATFACE', 'PIER PRESSURE' ] //list of boats
DOCK HOLIDAY //boat names as they're being instantiated
BOATY MCBOATFACE
PIER PRESSURE
[ CurrentBoatInfo {}, CurrentBoatInfo {}, CurrentBoatInfo {} ] //list of boat objects
undefined //the name of the first boat in the object list
[ CurrentBoatInfo {}, CurrentBoatInfo {}, CurrentBoatInfo {} ]
undefined


Thinking through this, I now think that my issue is that the createServer code runs, but doesn't log, and then when I access 127.0.0.1:1337, it logs name (which when instantiated was undefined)... but how do I make createServer wait for the objects to be instantiated?

Answer

The obvious problem I can spot is this piece of code:

docs.forEach(function(entry, index, array) {
  currentBoatList.push(entry.BoatName); //create list of boats
  if (currentBoatList.length === array.length) {
    console.log(currentBoatList);
    async.eachOf(currentBoatList, function(entry,index,array) {     
      BoatObjectList[index] = new CurrentBoatInfo(currentBoatList[index]); //create object of the boat's info
    }, server()); //create the server after creating the objects
  }
});

The problem here is that the async.eachOf is asynchronous function running inside the synchronous docs.forEach function. Also, the second parameter of the async.eachOf should have a callback which must be called for every item in the array, as in:

    async.eachOf(array, function(item, index, callback) {     
      doSomethingAsync(function(err, result) {
         if (err) return callback(err);

         // do something with result
         callback();
      });
    }, function(err) {
      // this is called when all items of the array are processed
      // or if any of them had error, err here will contain the error
      if(err) {
        console.log("something went wrong", err);
      } else {
        console.log("success");
      }
  });

As you can see running the server() as a callback doesn't look right because you should make sure there is no error passed to the final callback first and then proceed.

For your case I don't see why you are using async.eachOf instead of simple currentBoatList.forEach because you are not doing any asynchronous operations inside the loop, you are just populating the BoatObjectList.

UPD your problem is that you are not waiting for your asynchronous operations to finish before you are assuming your variables are ready. I hope the following will give you the idea of how you should achieve what you need (note that I've renamed some variables and functions just to make them more clear and easy to understand):

database.collection('Vessels').find(query).toArray(function(err, vessels) {
  if (err) {
    // process error
    return;
  }

  async.eachOf(vessels, function(vessel, index, next) {
    currentBoatList.push(vessel.BoatName);

    getVesselInfoByName(vessel.BoatName, function(err, info) {
      if (err) {
        return next(err);
      }

      vesselsInfoList.push(info);
      next();
    });
  }, function(err) {
    if (err) {
      // process error
      return;
    }

    // all vessels are processed without errors, vesselsInfoList is ready to be used
    server();
  });
});

function getVesselInfoByName(vesselName, callback) {
  // do everything you need to get vessel info
  // once you have received the vesselInfo call callback

  // if any error happens return callback(err);
  // otherwise return callback(null, vesselInfo);
}

Generally speaking I would advise you to find out more about how node.js asyncronius functions work and then have a closer look at the async library documentation.