Callbacks and Higher Order Functions in JavaScript

If you’ve used JavaScript before, you’ll have encountered a certain type of function called a higher order function — you may have not recognized it back then, but higher order functions are littered throughout JavaScript. jQuery, the popular framework is based on the concepts of callbacks and higher order functions.

For too long, I was stuck on the concept of callbacks and higher order functions and the resources online, while helpful didn’t quite explain it how I wanted it explained.

If you’re confused about callbacks and higher order functions like I was, hopefully this blog post can help clarify a few things.

Firstly, I’m going to assume that you knows what Functions are and how they operate in the JavaScript language. If you don’t, the Mozilla Development Network has a pretty good introduction to what functions are and you should read that before progressing.

Secondly, I’ll be using ES6 when I go through code examples — I have provided some ES5 functions as a reference, but throughout my posts ES6 arrow functions will remain the preferred syntax and I will use the ES5 style only when necessary (e.g functions that require the this command or the arguments object)

Higher Order Functions

In JavaScript, functions are first-class objects. This means that functions essentially have the same properties as other objects and you can operate on them the same way you can do with an object. Specifically this means they can:

  • Be named by variables.
  • Be passed as arguments to functions.
  • Be returned as values of functions.
  • Be the value of an expression.
  • Be members of a list/tuple.

Antony J. T. Davie (1992)

The two embolden and italicized elements above underpin the entire basis of higher order functions. If functions can be passed down and returned by functions, why not use them?

Higher order functions are functions that take in a function as a parameter or return a function.

For example take the following functions

ES5

function introduce(firstName, lastName, callback) {
var fullName = firstName + ' ' + lastName
console.log('Hello ' + firstName + ' '+ lastName)
callback(fullName)
}
function greetings (name) {
console.log('It\'s a pleasure to meet you ' + name)
}
introduce('Identity', 'Crisis', greetings)

ES6 (with arrow functions and template strings)

'use strict'
const introduce = (firstName, lastName, callback) => {
let fullName = firstName + ' ' + lastName
console.log(`Hello ${firstName} ${lastName}`)
callback(fullName)
}
const greetings = (name) => {
console.log(`It's a pleasure to meet you ${name}`)
}
introduce('Identity', 'Crisis', greetings)

Judging from the code above, we should see two logs outputted to our console, ‘Hello Identity Crisis’ and ‘It’s a pleasure to meet you Identity Crisis’ . Let’s take a look at what Node.js returns:

Excellent.

As you can see from the code above, introduce is a higher order function because it takes in a function as a parameter and then invokes it.

A higher order function can also be one that returns a function. As follows:

ES5

function goodbyes () {
console.log('au revoir')
function inEnglish () {
console.log('goodbye')
}
return inEnglish
}

ES6 (with arrow functions and template strings)

'use strict'
const goodbyes = () => {
console.log('au revoir')
const inEnglish = () => {
console.log('goodbye')
}
return inEnglish
}

With the above codes, you’d have to call the function so that you can get it to work, either by assigning it to a variable and calling the variable eg var foo = goodbyes() and foo() or by invoking goodbyes twice like ()(). Run this Code in Node’s REPL to get a better understanding of it, like in the example below.

Callbacks

In our previous example, we looked at higher order functions with the function introduce. Let’s bring down the code and see how this pertains to callbacks.

'use strict'
const introduce = (firstName, lastName, callback) => {
let fullName = firstName + ' ' + lastName
console.log(`Hello ${firstName} ${lastName}`)
callback(fullName)
}
const greetings = (name) => {
console.log(‘It’s a pleasure to meet you ${name}`)
}
introduce('Identity', 'Crisis', greetings)

As we determined above, introduce is a higher order function as it takes in a function as a parameter. A callback is essentially, the function that is taken in as a parameter.

In this case, the callback is the function greetings — we are taking the greetings function and then invoking it in our function. This is how many applications are created and how much of asynchronous JavaScript is programmed.

Let’s take the above code and create another function. Let’s add a parting ways function, so that our overall code becomes this:

'use strict'
const introduce = (firstName, lastName, callback) => {
let fullName = firstName + ' ' + lastName
console.log(`Hello ${firstName} ${lastName}`)
callback(fullName)
}
const greetings = (name) => {
console.log(`It’s a pleasure to meet you ${name}`)
}
const partingWays = (name) => {
console.log(`It was a pleasure meeting you ${name}, I hope we meet each other soon`)
}
introduce('Identity', 'Crisis', greetings)
introduce('Identity', 'Crisis', partingWays)

If we plop this on to Nodejs, we should expect the first invocation of introduce to log ‘Hello Identity Crisis’ and ‘It’s a pleasure to meet you Identity Crisis’ and the second invocation of introduce to log ‘Hello Identity Crisis’ and ‘It was a pleasure to meet you Identity Crisis, I hope we meet each other soon’

Let’s see what we get:

Splendid.

As you can see, all we did to the introduce function was change the function that was passed in it and we got a different result.

As stated previously, a callback function is the function that we passed into our introduce function — in this example, it’s our greetings and parting ways functions.

You can think of a callback as passing in a list of commands to another function. For example, in our introduce code, we are passing a set of greetings instructions — effectively, what we are saying is to execute the greetings code only if we are introducing ourselves. This prevents the greetings function firing before we are introduced to the person.

Similarly, because we don’t want to part ways without saying goodbye, we once again introduce to the user and state our intention to part ways with them — once again preventing the parting ways function from firing before we talk to the user.

The nature of callbacks thus makes them extremely powerful in Asynchronous JavaScript because the programmer can define when they want each function to fire, giving them control over how events are executed.

Callbacks are used so often in JavaScript that there are code bases littered with callbacks calling callbacks resulting in the initial callbacks becoming higher order functions and so on. This eventually leads to a term in JavaScript development called Callback Hell.

Hopefully this blog post helped you.

Errata, Feedback, Questions and Comments can be directed to my twitter (@dooraven) or you can post a comment here.