rongved rongved - 5 months ago 15
Node.js Question

Bind variables to callback function

I have a few functions in my controller which call some database functions. All of these database functions do "error callbacks", meaning that if a database error occur they do a separate callback which handles the error. Example:

exports.referralComplete = function(req, res){
/*getting id etc.*/
db.startDatabaseConnection(function() {
db.flagReferralAsDone(id, function(success) {
db.endDatabaseConnection();
/*doing stuff on success*/
}, onError);
}, onError);

function onError(err, description) {
logger.error(description + ": " + err);
user.pageNotFound(req, res);
}
}


I have multiple functions similar to this, which are calling different database functions. The problem is that I at the moment have duplicated onError() into the scope of each of them, since I need the req and res variables when handling the error. I could of course pass res and req to the database function, and then pass them as arguments to the error callback, but I like think that there might be a better way.

So the question is: Is it possible to somehow bind res and req to a global onError callback function, in a way that I won't have to pass the variables as arguments to the db function?

I'm very new to node.js and javascript in general, so if there is a better way of handling errors, please let me know.

Answer

Binding is simple!

db.startDatabaseConnection(function(){
  // whatever
}, onError.bind(this, var1, var2));

You can learn more about binding by clicking this awesome link, even though the link is sort of long.

Here's a real basic demo

// a function
var something = function (a, b, c) {
  console.log(a, b, c);
};

// a binding of something with 3 defined args
var b = something.bind(null, 1, 2, 3);

// call b
b();
//=> 1 2 3

Behind the scenes, this is basically what's happening

// ES6
const myBind = (f, context, ...x) =>
  (...y) => f.apply(context, [...x, ...y]);

// ES5
var myBind = function(fn, context) {
  var x = [].slice.call(arguments, 2);
  return function() {
    var y = [].slice.call(arguments, 0); 
    return fn.apply(context, x.concat(y));
  };
};

var b = myBind(console.log, console, 1, 2, 3);

b();
// => 1 2 3

b(4,5,6)
// => 1 2 3 4 5 6