Recent from talks
Nothing was collected or created yet.
Callback (computer programming)
View on WikipediaThis article needs additional citations for verification. (September 2015) |

In computer programming, a callback is programming pattern in which a function reference is passed from one context (consumer) to another (provider) such that the provider can call the function. If the function accesses state or functionality of the consumer, then the call is back to the consumer; backwards compared to the normal flow of control in which a consumer calls a provider.
A function that accepts a callback parameter may be designed to call back before returning to its caller. But, more typically, a callback reference is stored by the provider so that it can call the function later; as deferred. If the provider invokes the callback on the same thread as the consumer, then the call is blocking, a.k.a. synchronous. If instead, the provider invokes the callback on a different thread, then the call is non-blocking, a.k.a. asynchronous.
A callback can be likened to leaving instructions with a tailor for what to do when a suit is ready, such as calling a specific phone number or delivering it to a given address. These instructions represent a callback: a function provided in advance to be executed later, often by a different part of the system and not necessarily by the one that received it.
The difference between a general function reference and a callback can be subtle, and some use the terms interchangeably but distinction generally depends on programming intent. If the intent is like the telephone callback – that the original called party communicates back to the original caller – then it's a callback.
Use
[edit]A blocking callback runs in the execution context of the function that passes the callback. A deferred callback can run in a different context such as during interrupt or from a thread. As such, a deferred callback can be used for synchronization and delegating work to another thread.
Event handling
[edit]A callback can be used for event handling. Often, consuming code registers a callback for a particular type of event. When that event occurs, the callback is called. Callbacks are often used to program the graphical user interface (GUI) of a program that runs in a windowing system. The application supplies a reference to a custom callback function for the windowing system to call. The windowing system calls this function to notify the application of events like mouse clicks and key presses.
Asynchronous action
[edit]A callback can be used to implement asynchronous processing.
A caller requests an action and provides a callback to be called when the action completes which might be long after the request is made.
Polymorphism
[edit]A callback can be used to implement polymorphism. In the following pseudocode, say_hi can take either write_status or write_error.
from typing import Callable
def write_status(message: str) -> None:
write(stdout, message)
def write_error(message: str) -> None:
write(stderr, message)
def say_hi(write: Callable[[str], None]) -> None:
write("Hello world")
Implementation
[edit]The callback technology is implemented differently by programming language.
In assembly, C, C++, Pascal, Modula2 and other languages, a callback function is stored internally as a function pointer. Using the same storage allows different languages to directly share callbacks without a design-time or runtime interoperability layer. For example, the Windows API is accessible via multiple languages, compilers and assemblers. C++ also allows objects to provide an implementation of the function call operation. The Standard Template Library accepts these objects (called functors) as parameters. Many dynamic languages, such as JavaScript, Lua, Python, Perl[1][2] and PHP, allow a function object to be passed. CLI languages such as C# and VB.NET provide a type-safe encapsulating function reference known as delegate. Events and event handlers, as used in .NET languages, provide for callbacks. Functional languages generally support first-class functions, which can be passed as callbacks to other functions, stored as data or returned from functions.
Many languages, including Perl, Python, Ruby, Smalltalk, C++ (11+), C# and VB.NET (new versions) and most functional languages, support lambda expressions, unnamed functions with inline syntax, that generally act as callbacks. In some languages, including Scheme, ML, JavaScript, Perl, Python, Smalltalk, PHP (since 5.3.0),[3] C++ (11+), Java (since 8),[4] and many others, a lambda can be a closure, i.e. can access variables locally defined in the context in which the lambda is defined. In an object-oriented programming language such as Java versions before function-valued arguments, the behavior of a callback can be achieved by passing an object that implements an interface. The methods of this object are callbacks. In PL/I and ALGOL 60 a callback procedure may need to be able to access local variables in containing blocks, so it is called through an entry variable containing both the entry point and context information.[5]
Example code
[edit]C
[edit]Callbacks have a wide variety of uses, for example in error signaling: a Unix program might not want to terminate immediately when it receives SIGTERM, so to make sure that its termination is handled properly, it would register the cleanup function as a callback. Callbacks may also be used to control whether a function acts or not: Xlib allows custom predicates to be specified to determine whether a program wishes to handle an event. In the following C code, function printNumber() uses parameter get_number as a blocking callback. printNumber() is called with getAnswerToMostImportantQuestion() which acts as a callback function. When run the output is: "Value: 42".
#include <stdio.h>
#include <stdlib.h>
void printPumber(int (*getNumber)(void)) {
int val = getNumber();
printf("Value: %d\n", val);
}
int getAnswerToMostImportantQuestion(void) {
return 42;
}
int main(void) {
printNumber(getAnswerToMostImportantQuestion);
return 0;
}
C++
[edit]In C++, functors can be used in addition to function pointer. A functor is an object with operator() defined. For example, the objects in std::views are functors. This is an example of using functors in C++:
import std;
class MyCallback {
public:
void operator()(int x) {
std::println("Callback called with value: {}", x);
}
};
template <typename Callback>
void performOperation(int a, Callback callback) {
std::println("Performing operation on: {}", a);
callback(a);
}
int main() {
MyCallback callback;
int value = 10;
performOperation(value, callback);
return 0;
}
std::function<R(Args...)> is a type-erased wrapper for any callable objects, introduced in C++11:
import std;
using std::function;
void performOperation(int a, function<void(int)> callback) {
std::println("Performing operation on: {}", a);
callback(a);
}
int main() {
int value = 10;
performOperation(value, [](int x) -> void {
std::println("Callback called with value: {}", x);
});
return 0;
}
C#
[edit]In the following C# code,
method Helper.Method uses parameter callback as a blocking callback. Helper.Method is called with Log which acts as a callback function. When run, the following is written to the console: "Callback was: Hello world".
public class MainClass
{
static void Main(string[] args)
{
Helper helper = new Helper();
helper.Method(Log);
}
static void Log(string str)
{
Console.WriteLine($"Callback was: {str}");
}
}
public class Helper
{
public void Method(Action<string> callback)
{
callback("Hello world");
}
}
Kotlin
[edit]In the following Kotlin code, function askAndAnswer uses parameter getAnswer as a blocking callback. askAndAnswer is called with getAnswerToMostImportantQuestion which acts as a callback function. Running this will tell the user that the answer to their question is "42".
fun main() {
print("Enter the most important question: ")
val question = readLine()
askAndAnswer(question, ::getAnswerToMostImportantQuestion)
}
fun getAnswerToMostImportantQuestion(): Int {
return 42
}
fun askAndAnswer(question: String?, getAnswer: () -> Int) {
println("Question: $question")
println("Answer: ${getAnswer()}")
}
JavaScript
[edit]In the following JavaScript code, function calculate uses parameter operate as a blocking callback. calculate is called with multiply and then with sum which act as callback functions.
function calculate(a, b, operate) {
return operate(a, b);
}
function multiply(a, b) {
return a * b;
}
function sum(a, b) {
return a + b;
}
// outputs 20
alert(calculate(10, 2, multiply));
// outputs 12
alert(calculate(10, 2, sum));
The collection method .each() of the jQuery library uses the function passed to it as a blocking callback. It calls the callback for each item of the collection. For example:
$("li").each(function(index) {
console.log(index + ": " + $(this).text());
});
Deferred callbacks are commonly used for handling events from the user, the client and timers. Examples can be found in addEventListener, Ajax and XMLHttpRequest.
[6]
In addition to using callbacks in JavaScript source code, C functions that take a function are supported via js-ctypes.[7]
Red and REBOL
[edit]The following REBOL/Red code demonstrates callback use.
- As alert requires a string, form produces a string from the result of calculate
- The get-word! values (i.e., :calc-product and :calc-sum) trigger the interpreter to return the code of the function rather than evaluate with the function.
- The datatype! references in a block! [float! integer!] restrict the type of values passed as arguments.
Red [Title: "Callback example"]
calculate: func [
num1 [number!]
num2 [number!]
callback-function [function!]
][
callback-function num1 num2
]
calc-product: func [
num1 [number!]
num2 [number!]
][
num1 * num2
]
calc-sum: func [
num1 [number!]
num2 [number!]
][
num1 + num2
]
; alerts 75, the product of 5 and 15
alert form calculate 5 15 :calc-product
; alerts 20, the sum of 5 and 15
alert form calculate 5 15 :calc-sum
Rust
[edit]Rust have the Fn, FnMut and FnOnce traits.[8]
fn call_with_one<F>(func: F) -> usize
where F: Fn(usize) -> usize {
func(1)
}
let double = |x| x * 2;
assert_eq!(call_with_one(double), 2);
Lua
[edit]In this Lua code, function calculate accepts the operation parameter which is used as a blocking callback. calculate is called with both add and multiply, and then uses an anonymous function to divide.
function calculate(a, b, operation)
return operation(a, b)
end
function multiply(a, b)
return a * b
end
function add(a, b)
return a + b
end
print(calculate(10, 20, multiply)) -- outputs 200
print(calculate(10, 20, add)) -- outputs 30
-- an example of a callback using an anonymous function
print(calculate(10, 20, function(a, b)
return a / b -- outputs 0.5
end))
Python
[edit]In the following Python code, function calculate accepts a parameter operate that is used as a blocking callback. calculate is called with square which acts as a callback function.
def square(val: int) -> int:
return val ** 2
def calculate(operate: Callable[[int], int], val: int) -> int:
return operate(val)
# prints: 25
print(calculate(square, 5))
Julia
[edit]In the following Julia code, function calculate accepts a parameter operate that is used as a blocking callback. calculate is called with square which acts as a callback function.
julia> square(val) = val^2
square (generic function with 1 method)
julia> calculate(operate, val) = operate(val)
calculate (generic function with 1 method)
julia> calculate(square, 5)
25
See also
[edit]References
[edit]- ^ "Perl Cookbook - 11.4. Taking References to Functions". 2 July 1999. Retrieved 2008-03-03.
- ^ "Advanced Perl Programming - 4.2 Using Subroutine References". 2 July 1999. Retrieved 2008-03-03.
- ^ "PHP Language Reference - Anonymous functions". Retrieved 2011-06-08.
- ^ "What's New in JDK 8". oracle.com.
- ^ Belzer, Jack; Holzman, Albert G; Kent, Allen, eds. (1979). Encyclopedia of Computer Science and Technology: Volume 12. Marcel Dekker, inc. p. 164. ISBN 0-8247-2262-0. Retrieved January 28, 2024.
- ^ "Creating JavaScript callbacks in components". Archive. UDN Web Docs (Documentation page). sec. JavaScript functions as callbacks. Archived from the original on 2021-12-16. Retrieved 2021-12-16.
- ^ Holley, Bobby; Shepherd, Eric (eds.). "Declaring and Using Callbacks". Docs. Mozilla Developer Network (Documentation page). Archived from the original on 2019-01-17. Retrieved 2021-12-16.
- ^ "Fn in std::ops - Rust". doc.rust-lang.org. Retrieved 18 January 2025.
External links
[edit]- Basic Instincts: Implementing Callback Notifications Using Delegates - MSDN Magazine, December 2002
- Implement callback routines in Java
- Implement Script Callback Framework in ASP.NET 1.x - Code Project, 2 August 2004
- Interfacing C++ member functions with C libraries (archived from the original on July 6, 2011)
- Style Case Study #2: Generic Callbacks
Callback (computer programming)
View on GrokipediaActionListener interfaces for event handling, JavaScript's setTimeout for delayed execution, or OCaml's promise bindings for asynchronous workflows.[5] While powerful for composing behaviors, excessive nesting of asynchronous callbacks can lead to "callback hell," prompting alternatives like promises or async/await in modern languages.[6]
Fundamentals
Definition and Characteristics
In computer programming, a callback is a function that is passed as an argument to another function, known as the caller, which then invokes the callback at a later point to complete a task or process a result.[7] This mechanism allows the caller to specify custom behavior that executes in response to specific conditions or events within the caller's execution flow.[2] Unlike standard function calls where control returns immediately to the caller after execution, callbacks defer invocation, enabling flexible handling of outcomes without the caller needing to anticipate every detail in advance.[8] Key characteristics of callbacks include their support for both synchronous and asynchronous invocation. In synchronous callbacks, the caller invokes the callback immediately and waits for its completion before proceeding, similar to a direct function call but with the added layer of parameterization.[9] Asynchronous callbacks, in contrast, are invoked later—often after an external event or non-blocking operation completes—allowing the caller to continue execution without blocking.[10] Callbacks promote decoupling between the caller and the invoked logic, as the caller does not need to know the internal implementation details of the callback, only its interface. They typically support passing parameters from the caller to provide context and can return values to influence subsequent behavior, though in asynchronous scenarios, results are often handled via side effects or additional mechanisms. Additionally, callbacks enable recursion, where a callback invokes another instance of itself, and chaining, where one callback triggers subsequent ones to form pipelines of operations.[7] Callbacks facilitate inversion of control, a principle where the typical flow—high-level code directing lower-level routines—is reversed, with lower-level code invoking higher-level (client-provided) functions.[11] This shifts responsibility for execution timing and response handling to the framework or system, enhancing modularity without requiring tight coupling. To illustrate, consider the following pseudocode, where a sorting function accepts a callback to customize comparison:function sortArray(array, compareCallback) {
// Sorting logic here
for each pair in array {
if compareCallback(pair.first, pair.second) {
// Swap elements
}
}
return array;
}
// Usage: Pass a callback for custom ordering
sortArray(numbers, function(a, b) { return a > b; });
function sortArray(array, compareCallback) {
// Sorting logic here
for each pair in array {
if compareCallback(pair.first, pair.second) {
// Swap elements
}
}
return array;
}
// Usage: Pass a callback for custom ordering
sortArray(numbers, function(a, b) { return a > b; });
sortArray invokes compareCallback synchronously for each comparison, demonstrating parameter passing (a and b) and the callback's role in defining behavior.[2]
Historical Development
The concept of callbacks originated in the mid-20th century as part of interrupt handling mechanisms in early computer systems, where hardware events triggered specific software routines. As early as 1957, the TX-2 computer implemented an interrupt system that allowed asynchronous events to invoke designated handler code, providing a foundational model for deferred execution and event-driven responses.[12] By 1959, Christopher Strachey's proposal for priority-based interrupts in time-sharing systems further advanced this idea, enabling multiple users to share computing resources through vectored jumps to handler functions, patented in 1965.[12] These low-level assembly-based approaches influenced operating system design, such as the Compatible Time-Sharing System (CTSS) in 1961 and Multics in 1965, which used interrupt vectors and event processing loops to manage asynchronous I/O and user interactions, prefiguring callback patterns in higher-level programming.[13] Earlier, languages like Lisp (1958) supported higher-order functions, laying groundwork for callbacks in functional programming. A significant milestone occurred in the 1970s with the introduction of function pointers in high-level languages, facilitating callbacks beyond assembly. Dennis Ritchie developed the C programming language between 1969 and 1973 at Bell Labs, incorporating typed pointers—including function pointers—around 1971-1973, building on influences from BCPL (mid-1960s) and B (1969-1970), where pointers treated functions as addressable entities.[14] This allowed programmers to pass references to functions as arguments, enabling flexible, reusable code for tasks like signal handling in Unix, which Ritchie co-authored.[14] C's function pointers became a cornerstone for callbacks in systems programming, promoting modularity without direct reliance on hardware interrupts. In the 1980s, callbacks proliferated in graphical user interface (GUI) frameworks, adapting low-level mechanisms for interactive applications and emphasizing event-driven paradigms over polling. This design influenced subsequent GUI systems. By the 1990s, object-oriented languages integrated callbacks more seamlessly; for example, Java applets, first demonstrated in 1995 and released with Java 1.0 in 1996, used interface-based callbacks (e.g., ActionListener) to handle web-embedded interactions, enabling dynamic content in browsers during the era's web expansion.[15] The evolution continued into the 2000s with abstractions that mitigated the complexities of raw pointers, drawing from functional programming paradigms originating in lambda calculus (1930s) and early implementations like LISP (1958). Microsoft .NET, launched in 2002, introduced delegates as type-safe, object-oriented wrappers for function references, supporting multicast invocation for scenarios like event notification.[16] This shift culminated in lambda expressions, added to C# in version 3.0 (2007), which streamlined anonymous function passing and higher-order functions, enhancing callback usage in asynchronous and functional-style code across modern languages.[17]Uses and Applications
Event-Driven Programming
In event-driven programming, callbacks serve as handler functions that are registered to respond to specific events, such as mouse clicks or key presses, within an event loop that continuously monitors and dispatches these occurrences to the appropriate handlers.[18] This mechanism integrates closely with the observer pattern, where event sources act as subjects that notify registered observers—implemented via callbacks—upon state changes, enabling a one-to-many dependency without tight coupling between components.[19] The use of callbacks in this paradigm offers key benefits, including the promotion of non-blocking operations that keep applications responsive by avoiding long-running tasks in the main thread, as the event loop processes events asynchronously.[20] Additionally, callbacks facilitate decoupling between event producers (like input devices) and consumers (such as application logic), allowing modules to interact loosely through event notifications rather than direct invocations, which enhances modularity and maintainability.[21] Callbacks are prevalent in graphical user interface (GUI) toolkits, where they handle user interactions; for instance, the Windows API employs window procedures as callbacks to process messages like WM_LBUTTONDOWN for mouse events in an event loop.[22] Similarly, Qt's signals and slots mechanism functions as a typed callback system for connecting event emitters to handlers in cross-platform GUIs.[23] In real-time systems, such as game engines, callbacks manage dynamic events like input updates; Microsoft's GameInput API, for example, registers callbacks for device state changes to ensure low-latency responses in gaming applications.[24] Despite these advantages, managing callbacks in event-driven systems presents challenges, particularly in registration and deregistration to prevent memory leaks, as unreleased callback references can retain objects indefinitely in observer lists.[25] Improper handling may also lead to infinite loops or reentrancy issues, where a callback triggers another event that re-invokes the same handler, potentially causing stack overflows or unresponsive behavior in the event loop.[26]Asynchronous Programming
In asynchronous programming, callbacks enable non-blocking execution by allowing the main thread to continue processing other tasks while long-running operations, such as file I/O or network requests, are performed in the background. A callback function is passed as an argument to the initiating function and is invoked only upon completion of the operation, typically receiving the results or error information as parameters. This mechanism prevents the program from halting, improving responsiveness in environments like single-threaded runtimes.[27][28] The prominence of callbacks in asynchronous contexts surged with the advent of AJAX in web development around 2005, which leveraged asynchronous HTTP requests to update page content dynamically without full reloads. However, extensive nesting of callbacks for sequential asynchronous operations often results in "callback hell" or the "pyramid of doom," where deeply indented code becomes hard to read, debug, and maintain.[29] To mitigate these challenges, subsequent paradigms like promises emerged, providing a way to chain asynchronous operations and handle errors more elegantly without nested callbacks, followed by async/await syntax for writing asynchronous code in a synchronous-like style. Despite these advancements, callbacks retain a foundational role in core asynchronous APIs across languages and runtimes. In Node.js, for instance, the EventEmitter class relies on callbacks to notify listeners when asynchronous events, such as data reception over networks, occur. Similarly, browser APIs like setTimeout and setInterval use callbacks to schedule code execution after specified delays, facilitating timed asynchronous behaviors.[30][31][32][33]Polymorphism and Higher-Order Functions
Callbacks enable higher-order functions by allowing functions to be passed as arguments, facilitating the dynamic selection of behavior at runtime without predefined type-specific implementations. This pattern is commonly used in algorithms such as sorting, where a callback provides a custom comparison logic to dictate element ordering, thereby adapting the function's operation to varying requirements.[34] By treating functions as first-class citizens, callbacks support polymorphic behavior through higher-order functions, permitting diverse behaviors for a single interface without the need for inheritance-based class hierarchies, which aligns closely with functional programming principles. This form of polymorphism contrasts with subtype polymorphism in object-oriented languages, offering flexibility through runtime function substitution rather than static type hierarchies. The primary benefits include enhanced reusability and extensibility of code; for example, higher-order functions like map and filter accept callback functions to apply arbitrary transformations or predicates to collections, promoting generic algorithms that work across data types and reducing duplication. These features foster modular designs where core logic remains unchanged while behaviors are customized via callbacks.[35] Despite these advantages, limitations arise. In dynamic languages, callbacks lack compile-time type verification, potentially leading to runtime type errors if the passed function mismatches expectations.[36] Additionally, the indirection of invoking callbacks introduces performance overhead from extra function calls, though optimizations can mitigate this in compiled settings.[37]Implementation Strategies
General Mechanisms
Callbacks are fundamentally implemented by passing references to functions—such as pointers, references, or higher-order function arguments—to another function or module, enabling the recipient to invoke the referenced function dynamically without prior knowledge of its exact form. This technique decouples the caller from the specific implementation, promoting flexibility and modularity across programming paradigms. Invocation proceeds through indirect calls, where the reference is dereferenced at runtime to execute the callback, often passing relevant data or state as arguments to process the event or result.[38][38] To handle execution context, callbacks frequently employ mechanisms like closures, which bundle a function with its lexical environment, ensuring persistent access to outer variables and scope even after the enclosing function returns. This preserves necessary state, such as object bindings (e.g., 'this' in object-oriented contexts), preventing loss of context during deferred invocation. In paradigms without native closures, alternative bindings like explicit context objects or partial application simulate this behavior to maintain the intended environment.[39][39] Callbacks operate in both synchronous and asynchronous modes, distinguished by their interaction with the execution flow and underlying threading models. Synchronous callbacks execute immediately and block until completion, suitable for straightforward, sequential processing like sorting comparators. Asynchronous callbacks, conversely, return control promptly to avoid blocking, integrating with concurrency models such as single-threaded event loops that queue and dispatch callbacks in response to external events like I/O completions. Error handling in asynchronous contexts demands explicit strategies, such as dedicated error parameters in callback signatures or retry mechanisms via re-registration, to propagate exceptions without halting the primary thread.[3][3][3] Common design patterns for callbacks include interfaces that specify the expected signature and behavior, serving as contracts for interchangeable implementations. Registration tables, often implemented as lists or maps, facilitate managing multiple callbacks by allowing dynamic addition, removal, and invocation based on events, enhancing scalability in event-driven systems. These patterns underpin broader abstractions like the observer model, where subjects maintain callback registries to notify dependents.[40][40][38] Performance considerations arise from the inherent overhead of indirect function calls in callbacks, including dereferencing pointers, parameter passing, and potential stack management, which can introduce latency in high-frequency scenarios. Optimization techniques, such as compiler-directed inlining, eliminate this overhead for small, frequently invoked callbacks by embedding their code directly at call sites, though applicability is limited to non-recursive or non-virtual cases to avoid code bloat.[41][41]Language-Specific Variations
In imperative languages like C, callbacks are typically implemented using function pointers, which store the address of a function and allow it to be passed as an argument to another function for later invocation.[42] This approach provides low-level control but requires manual management of types and memory, potentially leading to errors such as dangling pointers if the function is deallocated prematurely.[42] In contrast, .NET languages such as C# use delegates, which are type-safe wrappers around method references that encapsulate callbacks while enforcing signature compatibility at compile time.[43] Java achieves similar functionality through interfaces, where classes implement a callback interface to provide methods that can be invoked polymorphically, ensuring type safety without direct function pointers.[44] Functional and scripting languages emphasize first-class functions, treating them as values that can be passed, returned, or stored, a paradigm influenced by Lisp's early support for such features through its treatment of functions as objects in the environment.[45] In Python, lambdas serve as anonymous functions for concise callbacks, while closures capture enclosing scope variables, enabling stateful callbacks without global pollution.[46] JavaScript similarly leverages first-class functions for callbacks, often in asynchronous contexts, where functions are passed to higher-order functions like event handlers, promoting flexible but potentially error-prone nesting known as "callback hell."[1] Modern systems languages introduce safety enhancements; Rust uses traits like Fn to define callable behaviors, allowing trait objects for dynamic dispatch in callbacks while leveraging the borrow checker to prevent data races and ensure memory safety at compile time.[47] Kotlin provides coroutines as an alternative to traditional callbacks for asynchronous programming, suspending execution without blocking threads and reducing nesting through structured concurrency, which simplifies error handling and cancellation compared to callback chains.[48] Cross-language interoperability poses challenges for callbacks, particularly via foreign function interfaces (FFI), where C's function pointers must be marshaled to higher-level representations, such as Python's ctypes callbacks that wrap Python functions for C invocation but risk garbage collection issues if not properly referenced.[49] Type systems significantly impact callback safety: even in dynamically typed languages like Erlang, extensions with type specifications can enable static verification of callback protocols, preventing mismatches in argument counts or types that could lead to runtime failures.[50] Advanced statically typed languages using dependent types or session types can provide even more sophisticated verification of callback protocols at compile time.[51]Programming Examples
C and C++
In C, callbacks are typically implemented using function pointers, which allow passing the address of a function as an argument to another function. A common example is theqsort function from the C standard library, which sorts an array and requires a user-provided comparator function via a function pointer. The comparator is declared with a specific signature: int (*compar)(const void *, const void *), where the arguments are pointers to the elements being compared, cast from void* to avoid type assumptions. To improve readability, a typedef is often used to alias this type, such as typedef int (*compar_t)(const void *, const void *);. [52]
Here is an example of using a callback with qsort to sort an array of integers:
#include <stdlib.h>
int compare_ints(const void* a, const void* b) {
int arg1 = *(const int*)a;
int arg2 = *(const int*)b;
if (arg1 < arg2) return -1;
if (arg1 > arg2) return 1;
return 0;
}
int main(void) {
int ints[] = {-2, 99, 0, -743, 2, INT_MIN, 4};
size_t size = sizeof ints / sizeof *ints;
[qsort](/page/Qsort)(ints, size, sizeof(int), compare_ints);
return 0;
}
#include <stdlib.h>
int compare_ints(const void* a, const void* b) {
int arg1 = *(const int*)a;
int arg2 = *(const int*)b;
if (arg1 < arg2) return -1;
if (arg1 > arg2) return 1;
return 0;
}
int main(void) {
int ints[] = {-2, 99, 0, -743, 2, INT_MIN, 4};
size_t size = sizeof ints / sizeof *ints;
[qsort](/page/Qsort)(ints, size, sizeof(int), compare_ints);
return 0;
}
<stdlib.h> for qsort and compiles with a standard C compiler like GCC via gcc example.c -o example. The void* parameters in the comparator enable generic sorting but require explicit casting, which can lead to type mismatches if not handled correctly. [52]
In C++, callbacks build on function pointers but extend them with higher-level abstractions for better safety and flexibility. Functors, or function objects, are classes or structs that overload the operator() to behave like functions, allowing them to maintain internal state. For instance, a functor can store comparison criteria as member variables, making it suitable for stateful callbacks. [53]
C++11 introduced std::function, a type-erasing wrapper from <functional> that can hold any callable object—such as function pointers, functors, or lambdas—with a matching signature, enabling polymorphic callbacks without exposing implementation details. [54] An example using std::function for a simple callback might wrap a lambda or functor and invoke it later.
Lambdas in C++ provide concise, inline callbacks with capture clauses to hold state by value ([=]) or reference ([&]), generating a closure object that acts as a functor. For example, std::function<void()> cb = [value = 42]() { std::cout << value; }; captures value by copy for use in asynchronous or event contexts. This requires including <functional> for std::function and compiles with C++11 or later standards, such as g++ -std=c++11 example.cpp -o example. [55]
A key mechanism in both C and C++ callbacks is passing context via void*, as seen in extended functions like C11's qsort_s, which adds a void* context parameter to the comparator for user data without global variables. However, this introduces risks: dangling pointers if the context outlives the callback invocation, or type mismatches from incorrect casts, potentially causing undefined behavior like segmentation faults. [52] [56]
JavaScript
In JavaScript, callbacks are functions passed as arguments to other functions, which are then invoked to handle specific events or asynchronous operations, enabling non-blocking execution in the language's single-threaded event loop model.[1] This approach is fundamental to JavaScript's event-driven architecture, particularly in web browsers and Node.js environments, where callbacks facilitate responsive user interfaces and efficient I/O handling without halting program flow.[57] A basic example of a callback involves thesetTimeout method, which schedules a function to execute after a specified delay. For instance, an anonymous function can serve as the callback:
setTimeout(function() {
console.log("Callback executed after 1000ms");
}, 1000);
setTimeout(function() {
console.log("Callback executed after 1000ms");
}, 1000);
setTimeout, which invokes it once the timer expires, demonstrating how callbacks defer execution.[33] Similarly, event listeners use callbacks to respond to user interactions; the addEventListener method attaches a callback function to an element, while removeEventListener detaches it using the same function reference to prevent memory leaks:
function handleClick() {
console.log("Button clicked");
}
const button = document.querySelector("button");
button.addEventListener("click", handleClick);
button.removeEventListener("click", handleClick); // Removes the listener
function handleClick() {
console.log("Button clicked");
}
const button = document.querySelector("button");
button.addEventListener("click", handleClick);
button.removeEventListener("click", handleClick); // Removes the listener
XMLHttpRequest. An onload callback handles successful responses:
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://example.com/api/data", true);
xhr.onload = function() {
if (xhr.status === 200) {
console.log("Data received:", xhr.responseText);
}
};
xhr.send();
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://example.com/api/data", true);
xhr.onload = function() {
if (xhr.status === 200) {
console.log("Data received:", xhr.responseText);
}
};
xhr.send();
setTimeout(function() {
// First async operation
xhr1.onload = function() {
// Second async operation
xhr2.onload = function() {
// Third async operation
console.log("All operations complete");
};
xhr2.send();
};
xhr1.send();
}, 1000);
setTimeout(function() {
// First async operation
xhr1.onload = function() {
// Second async operation
xhr2.onload = function() {
// Third async operation
console.log("All operations complete");
};
xhr2.send();
};
xhr1.send();
}, 1000);
=> instead of function keywords, which also lexically binds this to the enclosing scope—useful for preserving context in event handlers or timers:
setTimeout(() => {
console.log("Arrow function callback");
}, 1000);
button.addEventListener("click", () => {
console.log(this); // Refers to the button element's parent scope
});
setTimeout(() => {
console.log("Arrow function callback");
}, 1000);
button.addEventListener("click", () => {
console.log(this); // Refers to the button element's parent scope
});
.then() accepts arrow function callbacks), offering a partial mitigation to callback hell before async/await.[30]
In browsers, the EventTarget interface underpins callback mechanisms for DOM elements, providing standardized methods like addEventListener for dispatching events to registered callbacks across the event loop. Node.js extends similar patterns via its EventEmitter class, adapting callbacks for server-side events. Error handling within callbacks often employs try-catch blocks to manage exceptions without crashing the application:
xhr.onload = function() {
try {
const data = JSON.parse(xhr.responseText);
// Process data
} catch (error) {
console.error("Parse error:", error);
}
};
xhr.onload = function() {
try {
const data = JSON.parse(xhr.responseText);
// Process data
} catch (error) {
console.error("Parse error:", error);
}
};
Python
In Python, callbacks leverage the language's support for first-class functions, allowing them to be passed as arguments, returned from other functions, or assigned to variables, which facilitates functional programming paradigms suitable for scripting and data processing tasks.[62] This approach enables modular code where functions act as higher-order entities, briefly aligning with broader uses like polymorphism in higher-order functions.[62] A common technique for creating curried callbacks involves thefunctools.partial function, which partially applies arguments to a callable, producing a new function with preset parameters. For instance, to create a callback that always prints a specific message with a fixed prefix, one can use partial(print, "Processing: ") and invoke it later with variable suffixes.[63] This partial application supports currying by fixing initial arguments, making it ideal for configuring callbacks dynamically without rewriting full function definitions.[63]
Python's built-in map() function exemplifies a higher-order callback by applying a provided function—often a lambda expression—to each item in an iterable. For example, list(map(lambda x: x * 2, [1, 3, 5])) doubles each element, yielding [2, 6, 10], demonstrating how anonymous functions serve as concise callbacks in transformations.[64] This pattern is particularly useful in data processing pipelines where the callback defines the operation without needing a named function.[64]
Decorators provide a syntactic sugar for wrapping callbacks, enabling cross-cutting concerns like logging or timing without altering the original function's core logic. A timing decorator, for example, can measure execution duration using the time module:
import time
from functools import wraps
def timing_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.2f} seconds")
return result
return wrapper
@timing_decorator
def compute_sum(n):
return sum(range(n))
# Usage: compute_sum(1000000)
import time
from functools import wraps
def timing_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.2f} seconds")
return result
return wrapper
@timing_decorator
def compute_sum(n):
return sum(range(n))
# Usage: compute_sum(1000000)
@timing_decorator pattern wraps the callback compute_sum, logging its runtime automatically, preserving the original metadata via wraps.[65] Similarly, a logging decorator could prepend debug statements around the callback invocation.[65]
In asynchronous programming, Python's asyncio module uses callbacks to handle future completion via the add_done_callback method on Future objects. For example:
import asyncio
from functools import partial
async def set_after(fut, delay, result):
await asyncio.sleep(delay)
fut.set_result(result)
loop = asyncio.get_event_loop()
fut = loop.create_future()
loop.create_task(set_after(fut, 1, "Hello"))
fut.add_done_callback(partial(print, "Future done: "))
print(await fut) # Outputs: Future done: <Future...> then "Hello"
import asyncio
from functools import partial
async def set_after(fut, delay, result):
await asyncio.sleep(delay)
fut.set_result(result)
loop = asyncio.get_event_loop()
fut = loop.create_future()
loop.create_task(set_after(fut, 1, "Hello"))
fut.add_done_callback(partial(print, "Future done: "))
print(await fut) # Outputs: Future done: <Future...> then "Hello"
partial to pass additional arguments.[66]
Best practices for Python callbacks emphasize avoiding global state through closures, which encapsulate variables from enclosing scopes for private data access. For instance, a closure-based callback factory can bind configuration without globals:
def make_multiplier(factor):
def multiplier(x):
return x * factor # 'factor' is closed over
return multiplier
double = make_multiplier(2)
# Usage: double(5) -> 10
def make_multiplier(factor):
def multiplier(x):
return x * factor # 'factor' is closed over
return multiplier
double = make_multiplier(2)
# Usage: double(5) -> 10
typing.Callable to specify callback signatures, enhancing code readability and static analysis: def register_callback(cb: Callable[[str], None]) -> None: .... This denotes a callback accepting a string and returning nothing.[67]
Rust
In Rust, callbacks are typically implemented using closures, which are anonymous functions that can capture variables from their surrounding environment. These closures implement the standard library's function traits—FnOnce, FnMut, and Fn—which define how the closure interacts with captured data and how many times it can be invoked. The FnOnce trait is the most permissive, allowing a closure to be called once and potentially consume captured variables by moving them out; FnMut extends this by permitting multiple calls and mutable access to captured state without consumption; and Fn is the strictest, enabling multiple calls with only immutable access to captures, ensuring no mutation occurs.[68][69][47] These traits integrate with Rust's ownership and borrowing system, enforced by the borrow checker, to prevent issues like dangling references or use-after-free errors at compile time, making callbacks inherently safe without runtime overhead for simple cases.
For more flexible callback handling, especially when the exact closure type is unknown at compile time, Rust uses trait objects via dynamic dispatch with Box<dyn Fn>. This boxes the closure on the heap and erases its concrete type, allowing storage in collections or passing to functions that accept any implementation of the trait. For instance, a callback registry might store Vec<[Box](/page/Box)<dyn Fn()>> to hold diverse no-argument closures callable later. The dyn keyword indicates dynamic dispatch, which involves a virtual table (vtable) for runtime method resolution, introducing a small performance cost compared to static dispatch but enabling runtime polymorphism. Lifetimes must be explicitly managed in such trait objects; for example, [Box](/page/Box)<dyn Fn() + 'static> ensures the callback and its captures outlive the box, preventing lifetime violations in callbacks that borrow external data.[70]
Rust's concurrency model enhances callback safety through the Send and Sync traits, which the borrow checker uses to eliminate data races. A type implementing Send can be transferred between threads, while Sync allows safe sharing of references (&T) across threads; callbacks lacking these traits (e.g., those capturing non-thread-safe data like Rc<T>) fail to compile when used concurrently. For thread-safe callbacks, the std::sync module provides primitives like Arc (atomic reference counting) combined with Mutex to wrap closures, enabling shared mutable access: Arc<Mutex<Box<dyn FnMut() + Send + Sync>>> allows multiple threads to invoke the callback safely by locking the mutex. This setup prevents races by serializing access, with the borrow checker verifying trait bounds upfront. Error propagation in callbacks often uses Result<T, E>, where closures return results that callers can handle with the ? operator or pattern matching, ensuring failures bubble up without panics in safe code.[71][72][73]
In asynchronous programming, Rust supports callbacks via the Tokio runtime, where tasks are spawned using tokio::spawn and can incorporate closures for completion notifications or event handling. For example, a task might accept Box<dyn FnOnce(Result<(), [Box](/page/Box)<dyn std::error::Error>>) + Send + 'static> to invoke a callback upon finishing, with Send ensuring thread-safety across the runtime's worker threads and 'static handling lifetimes for async contexts. This integrates with Rust's async/await syntax, where callbacks must respect pinning and future lifetimes to avoid borrow checker errors during polling. Tokio's design leverages these traits to maintain zero-cost abstractions while preventing common async pitfalls like leaked tasks or races in callback invocations.
use std::sync::{Arc, Mutex};
use tokio::task;
fn main() {
let callback = Arc::new(Mutex::new([Box](/page/Box)::new(|| println!("Callback invoked!")) as [Box](/page/Box)<dyn FnMut() + Send + Sync>));
let shared_cb = Arc::clone(&callback);
task::spawn(async move {
// Simulate async work
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
shared_cb.lock().unwrap().call_mut(());
}).await.unwrap();
}
use std::sync::{Arc, Mutex};
use tokio::task;
fn main() {
let callback = Arc::new(Mutex::new([Box](/page/Box)::new(|| println!("Callback invoked!")) as [Box](/page/Box)<dyn FnMut() + Send + Sync>));
let shared_cb = Arc::clone(&callback);
task::spawn(async move {
// Simulate async work
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
shared_cb.lock().unwrap().call_mut(());
}).await.unwrap();
}
std::sync and Tokio, where the borrow checker ensures no invalid borrows occur across the spawn.[75]
Julia
In Julia, a high-performance language designed for scientific and numerical computing, callbacks are frequently employed through anonymous functions and the do-block syntax, enabling concise integration with higher-order functions and pipelines. The do-block syntax provides a convenient way to define inline callbacks for functions that accept a callable as their first argument, such as file I/O operations or data processing pipelines. For instance, the piping operator|> can chain data through an anonymous function acting as a callback: data |> x -> process(x) |> y -> output(y), where the anonymous function x -> process(x) serves as a modular callback for transformation steps in computational workflows.[76]
In numerical solvers, anonymous functions are commonly used as callbacks to inject custom logic during computation. For example, in the DifferentialEquations.jl package, a callback can be defined as an anonymous function to handle events like discontinuities or terminations in ordinary differential equation (ODE) solvers: solve(prob, Tsit5(), callback = function (integrator) integrator.u .*= 0.5 end), where the anonymous function modifies the state vector mid-simulation. This approach leverages Julia's metaprogramming capabilities to generate efficient, domain-specific solver behaviors without explicit function definitions.[78]
Callbacks integrate seamlessly with Julia's optimization libraries, allowing users to extend solver functionality. In JuMP, an algebraic modeling language for mathematical optimization, solver-independent callbacks enable the addition of lazy constraints, user cuts, or heuristic solutions during mixed-integer programming solves, which can dynamically refine objective evaluations by submitting improved feasible points. For custom logic in nonlinear optimization, the Optim.jl package supports a callback function invoked at each iteration, permitting early termination or logging based on convergence criteria, such as optimize(f, initial_x, options=Optim.Options(callback = (state) -> state.[iteration](/page/Iteration) > 100)). These mechanisms support tailored objective handling in scientific computing tasks like parameter estimation.[79][80]
Julia's just-in-time (JIT) compilation via LLVM impacts callback performance by introducing initial overhead on the first invocation, as the anonymous or do-block function is compiled to native code; however, subsequent calls execute at near-C speeds with minimal runtime penalty, making callbacks suitable for iterative numerical methods. Multiple dispatch further enhances polymorphic callbacks, dispatching to the appropriate method based on the types of callback arguments, such as solver state or data arrays, which optimizes execution in type-specialized scientific contexts without runtime type checks after compilation.[81][82]
The Julia package ecosystem extends callback capabilities for event-driven simulations, notably through CallbackSet in DifferentialEquations.jl, which composes multiple callbacks into a single handler for efficient event management in solvers, such as triggering reactions in stochastic models without sequential overhead. This structure supports scalable, high-performance event handling in domains like physics and biology simulations.[83]References
- https://tokio.rs/tokio/[tutorial](/page/Tutorial)/spawning
