Question

My setup is as follows:

  • Nodejs Server
  • server.js requires utils.js
  • utils.js loads data from mongodb into memory and exports it
  • server.js uses a variable that utils.js exports

The issue that I am worried about is the fact that the mongodb call is asynchronous. utils.js returns before the mongodb call is finished, meaning that server.js will use an undefined variable when it continues execution after the require.

What is the best to address this issue? The only thing I could think of is wrapping my server.js code in a giant callback and pass that to the function that makes the mongodb call. It seems a bit messy to me, is there a better way to do it?

Code:

server.js
var utils = require("./modules/utils.js");
console.log(utils);
//Do whatever

utils.js
var mods = [];
var db = require("mongojs").connect("localhost", ["modules"]);
db.modules.find({}, function(err, modules){
    mods = modules;
});
module.exports = mods;
Was it helpful?

Solution

What you're referring to is called "callback hell". The easiest way to get out of that is to use a Promise library that simplifies it.

I used a node package called bluebird.

var mysql = require("mysql");
var hash = require("password-hash");
var Promise = require("bluebird");
var settings = require("../settings");

Promise.promisifyAll(require("mysql/lib/Connection").prototype);
Promise.promisifyAll(require("mysql/lib/Pool").prototype);

var db_config = {
    user:settings.db.user,
    password:settings.db.password,
    database:settings.db.database
};

var con = mysql.createPool(db_config);

function query(sql) {
    return con.getConnectionAsync().then(function(connection) {
        return connection.queryAsync(sql)
        .spread(function(rows,fields) {
            return rows;
        }).finally(function() {
            connection.release();
        });
    });
}

This is a very basic database module I wrote that uses bluebird to promisify the database object.

And here's how it's used. It returns a promise! The benefit here is that not only does it return the clutter of callback hell, it makes sure that your code runs asynchronously and the function does not return before things have stopped processing, like in this case, a database query.

function login(user) {
    //check for player existance
    var query = 'SELECT p.name,p.password,p.id, pd.x, pd.y FROM player p INNER JOIN player_data pd ON p.id = pd.id WHERE p.name='+mysql.escape(user);
    return db.select(query).then(function(rows) {
        if (!rows.length) return;
        return [
            rows[0]
        ];
    });
}

Notice how you return a promise, so that you call the then or spread method to get those database values you just queried, not having to worry about if rows will be undefined by the time you want to use it.

OTHER TIPS

As you say, you need to wrap the entire server in a callback. Node.js works this way, it's asynchronous by nature. A server needs to pass through 3 stages: init, serve and deinit. In your case, that database code goes inside the init stage. You could write something like this.

//server.js
var utils = require ("./modules/utils");

var init = function (cb){
  //init the utils module, the http server and whatever you need
  utils.init (function (error){
    if (error) return handleError (error);
    cb ();
  });
};

var serve = function (){
  //Start listening to the http requests
};

var deinit = function (cb){
  //This is typically executed when a SIGINT is received, see link1
};

init (serve);

//utils.js
//You could write a wrapper for the db instance, see link2
var mongodb = require ("mongojs");
var db;

module.exports.init = function (cb){
  db = mongodb.connect ("localhost", ["modules"]);
  db.modules.find ({}, function (err, modules){
    if (err) return cb (err);
    cb (null, modules);
  });
};

I don't recommend using promises, they are slower than raw callbacks. You don't need them at all.

link1 - SIGINT
link2 - db wrapper

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top