When is a Function considered a Callback Function?

On the Thinkful Slack channel last week, someone asked a seemingly simple question about functions inside another function’s arguments:

within a route like app.get(β€˜/test’, someFunction(), (req, res) => etc etc, is someFunction() called an inner function? Does anyone know the technical name for that?

They got a very good answer which I think was close to perfect:

inner is more a generic term, not specific to functions. e.g. you could refer to inner and outer scope

  1. .get() is a higher order function because it takes one or more functions as a parameter

  2. someFunction is often termed a “callback” because it’s a function that’s intended to be called at a later time

  3. both someFunction and (req, res) => {} defined above are callback functions

They were right about the first part, that .get() was a higher order function.

They were right about the second part, that someFunction implies it can be called later.

They were even right about the final final part, where they called the anonymous inline ES6 “fat arrow” function (req, res) => {} a callback.

However, something kept nagging me about the difference between someFunction() in the question and someFunction in the final answer. Hidden in that lack of parentheses is a world of difference.

To elaborate, I offered the following comment to the channel, which sparked a conversation for further clarification. I reproduce it here in the hopes that it may help more programming students understand function semantics better.

I do not think someFunction in the line app.get('/test', someFunction(), (req, res) => {} can be considered a “callback”, because it’s not being passed by reference so nobody else is calling it.

You are invoking it with parentheses (calling it), which doesn’t mean “take the named function and put it right here in this line”, it means “take the named function, run it with [in this case, empty] parameters, take its return value, and then put it right here in this line”.

Passing by reference (like someFunction without ()) can be considered to be using the “callback” design pattern because the inner function you pass to the higher order function outside, can be called by the higher order function to return code control back to you. If you just call the function right now, there’s no passing of control, so there’s no back… it’s just a normal function call.

So if you did pass someFunction by reference rather than passing its returned value, it would be a type of callback function, but specifically in the context of Express, this would be a middleware function, and would be passed (req, res, next) as arguments when called.

Side note: next is also a callback function, it’s just one defined by Express rather than you, so you can call it to return control back to Express to keep processing more routes.

Your own (req, res) => {} middleware function can be correctly called an inline function which is, perhaps, the term you were originally asking about, meaning it exists as a literal value in your code, somewhere inside the (parentheses) of some other line of code (as opposed to the function being defined on its own line).

It is also possible that someFunction() returns a function(req,res,next){} object, such as is the case with express.static(), bodyParser.json(), or any other library function you would call from within an app.use( ... ). In this case, it would be more correct to say that “the result of someFunction()” is the callback, not “someFunction” itself.

So, just be careful when you talk about functions, to be specific about whether you are talking about the function itself (the action) or its return value (the result). And if the return value is also a function, then be sure you have different words to use for each level (such as “library function”, “module function”, or “factory function” for the outer layer and “middleware function” or “callback function” or “returned function” for the inner layer).

And if you want to use “inner function”, in the context of a function which returns a function, to mean the returned function, then go for it. Just know that is exactly what you mean, and be able to explain it πŸ™‚

This prompted a reply from a third person, trying to reconcile what I said with what MDN said about the official definition of a “callback function”.

I think your insight was super valuable @victorb, thanks for the in-depth response πŸ™‚ However, there is one point I’m not quite sure is 100% correct:

> I do not think someFunction{…} can be considered a “callback”, because it’s not being passed by reference so nobody else is calling it.

In my years as a JS developer, I’ve never heard of a callback stopping being referred to as a “callback” just because it’s not passed by reference. I thought I might be in the wrong here, but a quick MDN search yielded this:

https://developer.mozilla.org/en-US/docs/Glossary/Callback_function
> A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.

This never states that the function has to be passed by reference. And while I may sometimes doubt my own judgement, I do consider MDN a solid authority in this field πŸ˜‰

Plus, I do not believe that an inline function and callback are mutually exclusive terms. I mean, why would they be? An inline function can still be called just as well as a reference function, making it no lesser as a callback.

Though I do agree with your statement that the right term to refer to someFunction in the Express.js context is absolutely correct as Middleware.

Upon reading the definition quoted from MDN, I saw no problem with defining a callback function that way. I think the misunderstanding, once again, is in the difference between a function reference like someFunction, and its return value when that function is invoked — someFunction().

I further elaborated:

I would agree with that MDN definition 100%. Let’s break it down:

  1. “a function (a) passed into another function (b) as an argument” means something in the form of function b(a)
  2. “which is then invoked inside the outer function (b)” means that the body of b, somewhere, must contain the code a()
  3. “to complete some kind of routine or action” means that the call to inner a from within outer b happens at the end of b‘s normal execution.
  4. Ultimately this translates into a code pattern like the following. Note that I have kept the same numbers in the comments, to match the steps above from the MDN definition:

    // 1. pass an inner a callback function to an outer b function
    function b(a){
        // 3. do some kind of routine or action:
        getSomeAsyncThing( function( asyncResult ){
            // 2. invoke inner function a inside outer function b
            a(asyncResult);
        } ); 
    } // end function b

    So we can see here that in this literal example, the inner a function does not appear with parentheses until the inside of the outer function is being run.

    As a exists in the function argument parentheses, it does not have parentheses of its own, so therefore it must be passed by reference from outside b in order to be called by reference from inside b.

    The proper way to call the code defined above is b( a ) and not b( a() ).

    If you invoke the function a within the parentheses of b‘s calling point, then you are really passing a‘s return value to b instead. If that return value is not another function, like for instance if it is a number, string, array, or object, then you will get TypeError: a is not a function because essentially you’ve forced the inside of b to run a()() instead of a()

    I think “inner” can be used to describe any function which exists in the context of another function. An inner function can be defined within the body of another function, or passed as the argument to an outer function. In a recursive algorithm, the inner function becomes the outer function to a new layer.

    So, it’s all relative. The terms “inner” and “outer” are general logical terms which can be applied to any number of code patterns depending on the context.

    Passing by reference here means some name points to the memory location where that function lives, and it’s that name/location which we pass, rather than the function itself. That lets our calling function deal with our callback function however it wants, in its own time.

    Passing by value means we take the function and resolve it to its return value, and then pass that.

    The third way is passing by anonymous function literal, which creates a function on-the-fly in memory without a name to go with its location. This prevents function re-use, but otherwise is identical to a normal pass-by-reference.