1. Introduction

In this tutorial, we’ll find out what callback functions are and what to use them for.

2. What Are Callback Functions?

Callback functions are functions that we feed as parameters to other functions. The presumption is that the piece of code that receives a callback function as an argument will ‘call’ it inside its definition.

Callback functions are also known as ‘call-after’ functions, as they are often executed when another code block has finished. Programming languages support these types of routines in different ways, such as lambda functions or function references.

2.1. Example

Formally, we can describe callback functions as follows. Let’s assume that we have two functions: f and g. f produces some output which g processes to compute its results.

In the usual case, our main program runs f and calls g on the returned value:

Non-callback scenario

In the case of a callback, we pass g as an argument to f:Callback scenarioThe callback approach implies that somewhere inside the function f‘s body, there’s a call to g.

3. Synchronous Callbacks

A common callback is any function that is called in another function. Common callbacks are an example of synchronous callbacks. These callbacks are blocking and are called before the initial function returns.

For example:

function printCallBack(someCallBackFunction):
    print(someCallBackFunction())

function integerCallBackFunction():
    return 42

function helloWorldCallBackFunction():
    return "Hello, world!"

// Usage
printCallBack(helloWorldCallBackFunction)
// prints "Hello, world!"

printCallBack(integerCallBackFunction)
// prints "42"

In the pseudocode, we see that the function printCallBack prints the return value of the callback function it receives as its argument. By supplying different callback functions, we print different results.

4. Callback Functions in Asynchronous Context

More interesting are the cases where the callbacks involve asynchronous calls.

4.1. Callbacks for Asynchronous Calls

Let’s take for example an update call to a database followed by a read call. If the update call is asynchronous, then the update might finish after the read request. In that case, the read operation’s result doesn’t contain the updated data. Using a callback function fixes this problem.

First, let’s look at the naive implementation without a callback:

async function updateDatabase():
    Perform an asynchronous database update
    return

function readDatabase():
    result <- read database request
    print(result)

// Execute
updateDatabase()
readDatabase()
// Might print the state before the update 
// due to the asynchronous nature of the update

This can produce the wrong result: the read operation may print the state the database was in before the update. That can happen if the read operation completes before the update.

We can avoid such incorrect reads by following the callback approach:

async function updateDBWithCB(callBackFunction):
    Perform an asynchronous database update
    Call callBackFunction()
    return

function readDatabase():
    result <- read database request
    print(result)

// Execute
updateDBWithCB(readDatabase)
// The read operation prints the updated state

Now, the read request is a callback function we give as a parameter to the update function. The callback is executed after the update function finishes and returns the updated data. So, the result we get is the current database state, not the one before the update.

4.2. Asynchronous Callbacks

In the case of service calls or functions that have a long or variable duration, we might not want to wait for the callback function to return. In such cases, we pass an asynchronous callback function as an argument for further execution when the original function finishes. Asynchronous callback functions can be executed after the original function has returned.

How is this different from the callback in the database example? There, we used a synchronous callback in an asynchronous context. In contrast, the callback is asynchronous in this use case. Most of the time, we use asynchronous callbacks for event handling (e.g., in Spring) or I/O operations. Common examples include error handling and system interrupts.

5. Real-World Examples of Callback Functions

We can find callback functions in many types of software. In this section, we present two common examples of callbacks: those in browsers and the ones we use to train neural networks.

5.1. Asynchronous Functions in Browsers

Asynchronous calls have become widespread in modern browser languages such as JavaScript. These calls have the benefit of not being blocked, so browsers remain responsive while processing calls in the background (and in parallel).

Callback functions handle the returned values of asynchronous calls since the browser doesn’t wait for the asynchronous calls to return before continuing with other tasks.

For example, let’s say that a website requests some data from an API when a user clicks a button. In a synchronous setting, a browser blocks until the API returns the data.

However, using an asynchronous callback function on click to receive and print the API data resolves the issue. With it, the user doesn’t wait for the execution of the API request to finish, and the website remains responsive.

5.2. Callback Functions for Neural Network Training

What’s more, callbacks can come in handy when training neural networks. Using callbacks, the training function can add functionality to high-level API training procedures. This allows us to incorporate features such as advanced logging, model saving, and early stopping.

What does this mean? Well, the callback functions are executed every time an epoch of training finishes, i.e, at the end of every training step. With callbacks, the user can specify additional operations for the API to execute after each epoch.

Here’s how we can give callbacks to a model’s fit function in the Keras API:

my_callbacks = [
    tf.keras.callbacks.EarlyStopping(patience=2),
    tf.keras.callbacks.ModelCheckpoint(filepath='model.{epoch}-{val_loss}.h5')
]
model.fit(dataset, epochs=10, callbacks=my_callbacks)

Here, we’ve added early stopping with patience 2 and automatic model checkpoints to the callback routine.

6. Conclusion

In this article, we’ve shown what callback functions are and what they’re meant for. Additionally, we gave some examples of how callback functions are commonly used.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.