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.

Answers:

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.call(context, ...x, ...y);

// ES5
var myBind = function(fn, context) {
  var x = Array.prototype.slice.call(arguments, 2);
  return function() {
    var y = Array.prototype.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 

Context?

Context allows you to dynamically change the this of your function. Note you can only bind the context of functions defined with the function keyword; arrow functions have a lexical this that cannot be manipulated. This is shown for sake of completeness, but I do advise against this kind of program. It's usually better to just use another function parameter instead of relying on dynamic function context, this. Supporting context switching like this is to enable object-oriented style in JavaScript. Unless you are using this style, I see no reason to pay attention to context.

const getCanvas = (id) =>
  document.getElementById(id).getContext('2d')

const draw = function (canvas, x = 0, y = 0)
{ canvas.beginPath()
  canvas.strokeStyle = this.color             // `this` refers to context!
  canvas.rect(x, y, this.width, this.height)  // `this` refers to context!
  canvas.stroke()
}

// create two contexts
const contextA =
  { color: 'blue', width: 10, height: 10 }
  
const contextB =
  { color: 'green', width: 10, height: 20 }

// bind the draw function to each context and the canvas
const drawA =
  draw.bind(contextA, getCanvas('main'))
  
const drawB =
  draw.bind(contextB, getCanvas('main'))

// call the bound drawing functions normally
// draw three blue squares
drawA(0, 0)
drawA(20, 0)
drawA(40, 0)

// and one green rect
drawB(80, 0)
<canvas id="main"></canvas>

Partial application

Similar to binding is Partial Application

In computer science, partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.

Here we could make a very simple partial helper procedure which helps us accomplish this

const identity = x =>
  x

const partial = (f = identity, ...x) =>
  (...y) => f (...x, ...y)

const foo = (...all) =>
  console.log ('array of', all)

partial (foo, 1, 2, 3) (4, 5, 6)
// 'array of', [ 1, 2, 3, 4, 5, 6 ]


Currying

Currying is related to, but not the same as, binding or partial application

In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument.

const identity = x =>
  x

const curry = (f = identity, arity = f.length) => x =>
{
  const next = (xs = []) =>
    xs.length >= arity
      ? f (...xs)
      : x => next ([ ...xs, x ])
  return next ([ x ])
}

const foo = (a, b, c) =>
  console.log ('a', a, 'b', b, 'c', c)

curry (foo) (1) (2) (3)
// 'a' 1 'b' 2 'c' 3

curry (foo) ('choo') ('bye') ()
// 'a' 'choo' 'b' 'bye' 'c' undefined

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.