After the others responded, you stated that your problem were local variables. It seems an easy way to do this is to write one outer function to contain those local variables, then use a bunch of named inner functions and access them by name. This way, you'll only ever nest two deep, regardless of how many functions you need to chain together.
Here is my newbie's attempt at using the mysql
Node.js module with nesting:
function with_connection(sql, bindings, cb) {
pool.getConnection(function(err, conn) {
if (err) {
console.log("Error in with_connection (getConnection): " + JSON.stringify(err));
cb(true);
return;
}
conn.query(sql, bindings, function(err, results) {
if (err) {
console.log("Error in with_connection (query): " + JSON.stringify(err));
cb(true);
return;
}
console.log("with_connection results: " + JSON.stringify(results));
cb(false, results);
});
});
}
The following is a rewrite using named inner functions. The outer function with_connection
can be used as a holder for local variables, too. (Here, I've got the parameters sql
, bindings
, cb
that act in a similar way, but you can just define some additional local variables in with_connection
.)
function with_connection(sql, bindings, cb) {
function getConnectionCb(err, conn) {
if (err) {
console.log("Error in with_connection/getConnectionCb: " + JSON.stringify(err));
cb(true);
return;
}
conn.query(sql, bindings, queryCb);
}
function queryCb(err, results) {
if (err) {
console.log("Error in with_connection/queryCb: " + JSON.stringify(err));
cb(true);
return;
}
cb(false, results);
}
pool.getConnection(getConnectionCb);
}
I had been thinking that perhaps it would be possible to make an object with instance variables, and to use these instance variables as a replacement for the local variables. But now I find that the above approach using nested functions and local variables is simpler and easier to understand. It takes some time to unlearn OO, it seems :-)
So here is my previous version with an object and instance variables.
function DbConnection(sql, bindings, cb) {
this.sql = sql;
this.bindings = bindings;
this.cb = cb;
}
DbConnection.prototype.getConnection = function(err, conn) {
var self = this;
if (err) {
console.log("Error in DbConnection.getConnection: " + JSON.stringify(err));
this.cb(true);
return;
}
conn.query(this.sql, this.bindings, function(err, results) { self.query(err, results); });
}
DbConnection.prototype.query = function(err, results) {
var self = this;
if (err) {
console.log("Error in DbConnection.query: " + JSON.stringify(err));
self.cb(true);
return;
}
console.log("DbConnection results: " + JSON.stringify(results));
self.cb(false, results);
}
function with_connection(sql, bindings, cb) {
var dbc = new DbConnection(sql, bindings, cb);
pool.getConnection(function (err, conn) { dbc.getConnection(err, conn); });
}
It turns out that bind
can be used to some advantage. It allows me to get rid of the somewhat ugly anonymous functions I've created that didn't do anything much, except to forward themselves to a method call. I couldn't pass the method directly because it would have been involved with the wrong value of this
. But with bind
, I can specify the value of this
that I want.
function DbConnection(sql, bindings, cb) {
this.sql = sql;
this.bindings = bindings;
this.cb = cb;
}
DbConnection.prototype.getConnection = function(err, conn) {
var f = this.query.bind(this);
if (err) {
console.log("Error in DbConnection.getConnection: " + JSON.stringify(err));
this.cb(true);
return;
}
conn.query(this.sql, this.bindings, f);
}
DbConnection.prototype.query = function(err, results) {
if (err) {
console.log("Error in DbConnection.query: " + JSON.stringify(err));
this.cb(true);
return;
}
console.log("DbConnection results: " + JSON.stringify(results));
this.cb(false, results);
}
// Get a connection from the pool, execute `sql` in it
// with the given `bindings`. Invoke `cb(true)` on error,
// invoke `cb(false, results)` on success. Here,
// `results` is an array of results from the query.
function with_connection(sql, bindings, cb) {
var dbc = new DbConnection(sql, bindings, cb);
var f = dbc.getConnection.bind(dbc);
pool.getConnection(f);
}
Of course, none of this is proper JS with Node.js coding -- I just spent a couple of hours on it. But maybe with a little polishing this technique can help?
getSomeDate
andgetSomeOtherDate
ends up in changing the indentation of many lines which makes git history harder to read (git blame
is even useless after this), and you likely make bugs when doing this manually – Aquamarine