Message Systems in Programming: Part 2 of 7 – Callbacks

Callbacks

Callbacks are a way be notified of an event and not have to care if it’s synchronous or asynchronous. This could happen immediately or some time later. It’s the “don’t call me, I’ll call you” of programming. It also gives the receiver the power to dictate where they message goes and usually in what scope. In languages that do not natively support blocking, asynchronous programming needs some mechanism to tell you when “things are done”.

// sender
var objectA = {
callback: null,
setCallback: function(callback)
{
this.callback = callback;
},
execute: function()
{
this.callback();
}
};
// receiver
var objectB = {
onDone: function()
{
console.log("done");
}
};
objectA.setCallback(objectB.onDone);
objectA.execute();

Callbacks are really useful in that they’re easy to understand, and relatively easy to debug. They solve direct class references by offering the external setting of a pointer, at runtime. They’re also flexible enough in dynamic languages to get around race conditions of those functions not having to be defined; you can simply use strings to dynamically access them on demand.

// sender
var objectA = {
callback: null,
scope: null,
setCallback: function(scope, callback)
{
this.callback = callback;
this.scope = scope;
},
execute: function()
{
this.scope[this.callback].call(this.scope);
}
};
// receiver
var objectB = {
onDone: function()
{
console.log("done");
}
};
objectA.setCallback(objectB, "onDone");
objectA.execute();

They also are centrally contained; meaning each class/module/object handles registering and unregistering the callback. There is no need for external libraries, and no race conditions if you dynamically access the callback method. It also offers a simple solution to asynchronous programming challenges.

It doesn’t matter when the callback handler is run, nor how many times. It runs in a synchronous manner. To turn if off, simply call setCallback with a null parameter. Most API’s and setCallback ‘esque functions are smart enough to not run if no callback is set for obvious reasons.

Another common use for callbacks are for networking. Most HTTP requests (getting data from a web server) take anywhere from milliseconds to many seconds to happen.

Here’s how Go does it; it’s a blocking language (sort of)

// Go doing the Parappa The Rappa Block
func main() {
fmt.Println("Before http.Get...")
resp, err := http.Get("http://jessewarden.com/")
fmt.Println("You won't see this until after the http.Get has happened.")  
}

Notice the code will pause execution (or block) on the http.Get line until the http.Get returns.

Node.js doesn’t block like some server-side languages, instead supporting the callback mechanism. Also known as “I don’t care how long you take, just call this function when done”.

// vs. the Callback way in Node.js which doesn't Block, Block, Block
function success(error, response, body)
{
console.log("tons of text:", body);
}
request("http://jessewarden.com", success);

You’ll see a lot of people in callback-common languages use anonymous/lambda functions inline for terser code:

request("http://jessewarden.com", function(error, response, body)
{
console.log("tons of text:", body);
});

In languages that support anonymous functions without an aggressive mark and sweeper in garbage collection, that will be more common place.

Here’s an example used for a game; when the user’s health decreases, we’ll have a health bar redraw the new value on a canvas.

// our View
var HealthBar = {
setHitPoints: function(value, total)
{
var percentage = value / total;
var newHealthWidth = canvasWidth * percentage;
// redraw canvas with red for the entire width,
// and only green up to the newHealthWidth
}
};

Our Model holds our health:

// our Model
var Health = {
current: 100,
max: 100,
callbackFunction: null,
takeDamage: function()
{
this.current -= 10;
},
setCallback: function(callback)
{
this.callbackFunction = callback;
},
runChangeCallback: function()
{
if(this.callbackFunction != null)
{
this.callbackFunction(this.current, this.max);
}
}
};

… and now we wire them up:

Health.setCallback(HealthBar.setHitPoints);

One class has to have a reference to the other, and the function/method has to exist unless you use Strings.

Let’s show an example using MVC. Our View class wraps JQuery HTML magic for listening to clicks, showing data in a text label, gives us a callback to handle user gestures. Our Model is the API to our data and lets us know via callbacks when it’s data changes. Our Controller marry’s them together; it listens for clicks in the View, and updates the View when data changes in the Model.

// our View
(function()
{
var View = function()
{
var callbackFunction = null;
return {
init: function()
{
var me = this;
$("#SomeButtonOrDiv").click(function()
{
me.runClickCallback();
});
},
showData: function(value)
{
$("#SomeField").value = "Count: " + value;
},
setClickCallback: function(callback)
{
callbackFunction = callback;
},
runClickCallback: function()
{
if(callbackFunction != null)
{
callbackFunction();
}
}
};
};
return View;	
}());

Only real difference here is that our callback function is private; as private as you can get in JavaScript wanna-be-classes.

Now, for our Model class, we define her like so:

// our Model
(function()
{
var Model = function()
{
var clickCount = 0;
var callbackFunction = null;
return {
increment: function()
{
clickCount++;
},
getClickCount: function()
{
return clickCount;
},
setChangeCallback: function(callback)
{
callbackFunction = callback;
},
runChangeCallback: function()
{
if(callbackFunction != null)
{
callbackFunction();
}
}
};
};
return Model;	
}());

Finally, our Controller in this case is just a bunch of code in index.html that wires everything up.

var ourView = new View();
var ourModel = new Model();
function whenSomeoneClicks()
{
ourModel.increment();
}
function whenDataChanges()
{
ourView.setValue(ourModel.getCurrentCount());
}
ourView.setClickCallback(whenSomeoneClicks);
ourModel.setChangeCallback(whenDataChanges);
// jump start this party
whenDataChanges();

A simple way for MVC elements to communicate.

Finally, some frameworks support the ability to send parameters to the callback. This is for concurrency situations where multiple callbacks could be called, perhaps in a random order, and you wish to know which callback response corresponds to which. Some frameworks utilize this behind the scenes to support first, multiple, or last scenarios. Meaning “I want to know only about the first request, no matter how many happen” or “many requests can happen, I don’t care about order”, or “I only want to know about the latest request, cancel any that are currently happening”.

Callback Scope

Scope is handled in a variety of ways. C#, Java, and ActionScript invoke the callback in the scope it is defined since they’re a class based language. When you pass in a function, you can rest assured your “this” keyword or language equivalent will be scoped correctly.

Lua allows 2 ways to define methods on objects, either using a . or :. If you utilize the colon, the “self” (analogous to “this”) will remain in intact regardless of where she’s called from. If you use the . the first parameter the callback is the scope of the invocation, and the later parameters are what the invoker passed.

local image = display.newImage('some.png')
function image.touch(scope, event)
-- scope: 	table: 0x7fe4cf407f90
print("scope: ", tostring(scope))
-- self: 	nil
print("self: ", tostring(self))
end
image:addEventListener("touch", image)
function image:touch(event)
-- self: 	table: 0x7fe4cf265280
print("self: ", tostring(self))
end
image:addEventListener("touch", image)

JavaScript usually uses a Delegate approach to transparently wrap the callback functions in a function to ensures scope remains intact. This is to help new JavaScript developers who don’t always understand the conditions why their “this” variable will stop working. This is commonly used via Underscore’s bind function, or transpilers like CoffeeScript.

There are some who utilize hoisted scope to avoid using this altogether, such as the vm variable within John Papa’s Angular Style Guide.

Specifically from the style guide on Github:

/* avoid */
function Customer () {
this.name = {};
this.sendMessage = function () { };
}
/* recommended */
function Customer () {
var vm = this;
vm.name = {};
vm.sendMessage = function () { };
}

John’s way has the advantages of not being a keyword so you can’t redefine it unless you intentionally hide it by accident, works no matter how deep your callbacks go, and it can be uglified in production code.

Callback Message Types

In dynamic languages, there are no conventions to what parameters callbacks spit back. Places where they are conventions are Lua which can pass scope, some Python frameworks which do similar behavior, and Node.js which always passes an error back as the 1st parameter or null if ‘success’, with any other arguments after that.

Frameworks like Django for Python have a set of conventions in the order of these parameters, usually the HTTP Request that invoked the View function.

Django will call methods on your View class with the HTTPRequest:

def login_user(request):
print "login_user"
print "method: " + request.method
print "body: " + request.body

Note that Django, while not using a “setCallback” method, actually uses a separate url’s file where you defined what URL’s call what function. That said, the effect is the same.

Callback Pros

Callbacks are simple. They work the same in multiple languages so are easy to implement in a variety of platforms. Some platforms, however, make this easier than others. For example, as you can see above, JavaScript & Lua scope issues come into play and you have to use various techniques (usually libraries or transpilers) to retain scope when the callback is executed, language features like Lua uses using the : character when defining methods on an object, or conventions to avoid them altogether. Strings can be used in the case of dynamically loading objects/classes or more dynamic use cases.

Callbacks are also flexible. Since they are just objects in most dynamic languages, you can pass them around and store them all over the place. For languages that allow anonymous functions to not be immediately garbage collected if they exist within a given scope, this allows terse code as well. This is especially true in dynamic languages like JavaScript and Lua where you don’t have to define the Object/Table’s variables ahead of time and just dynamically set/get them.

Finally, performance wise, callbacks are quite small as the actual callback function and possibly scope reference are just pointers. Even the scope fixers like Underscore’s bind just create new functions which in those types of languages this is ok since they are optimized for creating and using functions often inline.

Callback Cons

There are a few gotchas and downsides to callbacks. The first being there is no scope when the function is called; it’s executed in the scope of where it’s run in most languages, not where it’s defined. Suddenly your “this” or “self” break and you’re all like “wth!?”. The errors that arise from null pointer access of methods aren’t always obvious it’s a scope issue. Strongly typed languages usually don’t have this problem, thankfully. In dynamic languages, you end up following some team convention, using some library, or some transpiler/post processor.

The first fix is to provide 2 parameters to set callback: scope and then callback function. The second fix is to utilize a Delegate function previously mentioned (Underscore’s bind). Whether you utilize the scope approach or the Delegate approach, now all your classes/modules/objects have to store 2 internal properties vs. 1 for each callback you care about, or create an additional Function for each callback Function. For example, to support a click, touch, and whatever event, you’d need 6 total internal variables.

// lame code is lame
function setCallback(scope, callback)
{
this.callbackScope = scope;
this.callbackFunction = callback;
}
function setKeyboardCallback(scope, callback)
{
this.keyboardScope = scope;
this.keyboardFunction = callback;
}
function setDataCallback(scope, callback)
{
this.dataChangeScope = scope;
this.dataChangeFunction = callback;
}

If you chose to go the more sane Delegate route:

var someView = {
setCallback: function(callback)
{
this.callback = callback;
},
setKeyboardCallback: function(callback)
{
this.setKeyboardCallback = callback;
},
setDataCallback: function(callback)
{
this.dataCallback = callback;
}
};
var other = {
name: 'other',
onKeyboardPressed: function(event)
{
console.log("this.name should be 'other': " + this.name);
}
};
_.bind(other.onKeyboardPressed, other);
// this.name should be 'other': other

The 1st solution doesn’t scale so well. RAM wise, at least in V8, it’s actually not bad; I’m referring to developer pain and technical debt. The 2nd solution tends to solve the developer pain. Even with the myth of Object.prototype being always better, V8 and other JavaScript engines are optimized to help with the creation of inline functions. If you come from other languages where anonymous/inline/lambda functions are tacked on, they can often have a higher than normal memory and performance footprint and a bit more unwieldy to create.

The second issue as I’ve previously mentioned is if the method isn’t defined yet. For more aggressive JavaScript syntax checkers, when using non-anonymous/inline functions, or transpiled languages like TypeScript, you can run into this. You get into this with asynchronous modules or when someone is using the Decorator pattern at a later date to dynamically add methods to your class. The fix is to use a String name for the callback vs. a function reference. That way you can dynamically access it like so:

this.scope[this.callback].call(this.scope);

This can come in handy if you change your perspective by dynamically accessing callbacks as well for more terse and flexible code:

// notice you can change the power level to 1, 2, or 3
var ship = {
currentPowerLevel: 1,
fire1: function()
{
},
fire2: function()
{
},
fire3: function()
{
}
fire: function()
{
this["fire" + this.currentPowerLevel]();
}
};

Going back our HealthBar example, what if you have a 2nd or 3rd class that wants to listen to the Health changing? The current API only allows 1 dude to listen. Callbacks by default are a 1 to 1 relationship. That said, once you’ve “callbacked it”, you have to move onto other solutions like Events or EventEmitters if you want a 1 to many.

Finally, in callback convention frameworks like Node, you can get into a callback hell. Unlike a JQuery one, the Node one can be fixed by async or Promises, either ES6 or a library like Q or Bluebird.

<< Part 1 – Introduction | Part 3 – Events >>