Functional JavaScript – passing additional arguments to callback.

This post will show perfect use case for closure and higher order functions.

In work, I am developing websites in Erlang, but sometimes, there are things, that just have to be done on client side using JavaScript.
I knew JavaScript basics, but I wanted to truely know, what I am doing, so I picked up “Programming JavaScript Applications” [1].
For a quick reference, i also use JavaScript “JavaScript: The Good Parts” [2].
I like to get my hands dirty early, so I decided to make a simple game.
Searching for resources, I’ve found tutorial on building game main loop [3].

The main function looked like this:

var mainloop = function() {

and it was called like this:

var animFrame = window.requestAnimationFrame
var recursiveAnim = function(timestamp) {
    animFrame( recursiveAnim );
animFrame( recursiveAnim );

Actually, it was more complicated, but it is not important for making my point about functional programming.
If you want to see the details, check out orignal blog post [3].

There are two things, you have to know:

1. animFrame [4] takes a callback, that you want to call before repeainting the window.
This callback takes exactly one argument – a timestamp.

2. mainloop, updateGame and drawGame take no parameters,
so they just operate on variables, that are defined out of their scope.

I don’t like second point, I would like to write functions,
that don’t depend on external state – pure functions [5].
They are easier to test and reason about.
For example, updateGame function could take game state as an argument
and return new game state.
drawGame should also take canvas context and game state as arguments.
It won’t be pure, because it will draw to the canvas,
but at least, it will always work the same way, given the same inputs.

In short, I wanted something like this:

var mainloop = function mainloop(gameState, ctx) {
    var newGameState = updateGame(gameState);
    drawGame(newGameState, ctx);
    return newGameState;

But there is a problem: how to pass it to animFrame?
animFrame doesn’t know anything about game state or other variables,
that I want to pass to the mainloop.
This is a perfect job for closures [6].

I can create a function, that takes exactly one argument,
but “carries” two more from otuer scope:

function (timestamp) {
    //uses arguments from outside its scope
    //use only inside other function, that has gameState and ctx defined
    recursiveAnime(timestamp, gameState, ctx);

and use it like this:

var recursiveAnim = function(timestamp, gameState, ctx) {
    var newGameState = mainloop(gameState, ctx);
    animFrame( function (timestamp) {
        recursiveAnime(timestamp, newGameState, ctx);

// start the mainloop
animFrame( function (timestamp) {
    recursiveAnime(timestamp, newGameState, ctx);

This allows passing additional arguments to callback
without exposing newGameState or ctx to any other code.
Its already great, but it can be better.
Lets extract the pattern, that wraps three argument function into one argument function:

var toRecursiveAnim = function toRecursiveAnime(callback, gameState, ctx) {
    return function(ts) {
        callback(ts, gameState, ctx);

This one takes callback and two additonal arguments
and creates one argument function, which
calls given callback with those arguments.
Now, the code can be simplified:

var recursiveAnim = function(timestamp, gameState, ctx) {
    var newGameState = mainloop(gameState, ctx);
    animFrame( toRecursiveAnim(recursiveAnim, newGameState, ctx) );

// start the mainloop
animFrame( toRecursiveAnim(recursiveAnim, gameState, ctx) );

JavaScript allows extracting patterns and writing code, that is really easy to test.
This example showed good use case for closures and higher order functions in JavaScript.
Hope, you enjoyed it :)



Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s