PROGRAMMING-CONCEPTS

Callback: Definition, Purpose, and Examples

A callback is a function you pass into another function so it can be executed later. Instead of running immediately, a callback runs at the moment the receiving function decides, often in response to an event, a completed task, or a specific condition.

Callbacks make it possible to write flexible, reusable, and asynchronous code — especially in JavaScript, where many operations depend on actions that finish sometime in the future.


Why Callbacks Matter

A callback lets you hand over control.

You’re essentially saying: “When you’re done with your job, run this function next.”

They are essential for:

  • Handling user interactions (button clicks, form submissions)
  • Managing asynchronous operations (API calls, timeouts)
  • Customizing behavior (sorting, filtering, mapping data)
  • Organizing code into smaller, reusable functions
  • Avoiding repetition by letting users “plug in” specific behavior

Callbacks appear across the entire ecosystem — from Python sorting to React event handlers.


Core Idea: Passing a Function as an Argument

The defining feature of a callback is that it is a function used as a value.

JavaScript example:

function doSomething(later) {
  later();
}

Here, later is a callback.

The caller decides what later actually does.

Python works the same way:

def run_twice(func):
    func()
    func()

Passing functions around like data is what makes callbacks powerful.


Callbacks in JavaScript (Most Common Use Case)

JavaScript relies heavily on callbacks because so many operations are asynchronous.

Event callbacks

button.addEventListener("click", () => {
  console.log("Button clicked!");
});

The callback only runs when the click actually happens.

Timeout callbacks

setTimeout(() => {
  console.log("Done waiting!");
}, 1000);

The callback runs after one second.

Without callbacks, timing-based behavior would be awkward to express.

API request callbacks

Early JavaScript code often used callback-based APIs:

fetch("/api/data").then(response => {
  console.log("Data loaded");
});

Even this modern .then(...) form is callback-driven — promises simply wrap callbacks with a cleaner interface.


Callbacks in TypeScript

TypeScript adds types so you can define the shape of the callback.

function loadUser(id: number, callback: (name: string) => void) {
  const username = "Alice";
  callback(username);
}

The callback must accept a string.

Type safety is especially helpful when functions return complex values.


Callbacks in Python

Python uses callbacks in sorting, iteration, and higher-order functions.

Sorting with a callback

users = ["Charlie", "bob", "Alice"]

sorted_users = sorted(users, key=lambda name: name.lower())

The callback (lambda name: ...) determines how sorting compares values.

It gives you complete control over customizing behavior.

Applying a callback across a list

def apply_twice(func, value):
    return func(func(value))

This example shows how callbacks allow you to plug logic into a reusable structure.


Callbacks in Swift

Swift uses closures — Swift’s version of callback functions.

func fetchData(completion: (String) -> Void) {
    completion("Loaded successfully")
}

Swift’s design makes callback usage explicit but concise:

fetchData { message in
    print(message)
}

This pattern appears in nearly all asynchronous iOS APIs.


Callbacks in React

React components often rely on callbacks to handle events or communicate with parent components.

Passing a callback down to a child component

function Child({ onSelect }) {
  return <button onClick={() => onSelect("A")}>Choose A</button>;
}

function Parent() {
  const handleSelect = (value) => {
    console.log("Selected:", value);
  };

  return <Child onSelect={handleSelect} />;
}

The parent decides what happens when the child triggers the callback.

This pattern keeps components reusable and predictable.

Updating state with callbacks

setCount(prev => prev + 1);

React passes the previous state value into the callback to avoid stale data.

This is essential for reliable state updates when updates happen quickly.


Real-World Example: Filtering Data

Callbacks let you customize how data should be filtered or transformed.

JavaScript:

const products = [
  { name: "Phone", price: 800 },
  { name: "Cable", price: 10 }
];

const expensive = products.filter(p => p.price > 100);

The callback (p => p.price > 100) tells filter how to decide which items stay.

Without callbacks, the filter function would be fixed and far less flexible.

Python has the same idea:

expensive = [p for p in products if p["price"] > 100]

Although Python doesn't have a built-in .filter method on lists, the pattern is identical: the callback expresses your “decision rule.”


Real-World Example: Sorting

JavaScript:

const items = ["pear", "Banana", "apple"];

items.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));

The callback controls exactly how two items should be compared.

This lets you customize sorting for case-insensitive behavior, custom weights, or domain-specific rules.

Swift uses the same approach:

let sorted = items.sorted { $0.lowercased() < $1.lowercased() }

Sorting logic becomes modular and reusable thanks to callbacks.


When to Use Callbacks

Callbacks shine when:

You need to react to something

Events, button clicks, gestures, keypresses — callbacks run only when the moment arrives.

A task takes time

Network operations, reading files, waiting for timers — callbacks run when the task finishes.

You want to customize a built-in function

Sorting, filtering, transforming data — callbacks let you pass in custom rules.

You want to reuse a structure with different behavior

Functions like map, filter, event listeners, and iteration all rely on callbacks for flexibility.

Callbacks keep code flexible without rewriting the structure each time.


Callback Hell (What to Avoid)

Deeply nested callbacks were common in early JavaScript:

loadUser(id, user => {
  loadOrders(user, orders => {
    loadPayments(orders, payments => {
      console.log("Done!");
    });
  });
});

This “pyramid” shape is hard to read and maintain.

Modern JavaScript avoids callback hell using:

  • Promises
  • .then() chains
  • async/await
  • Modularizing callback logic

Callbacks remain useful, but structuring them well is essential.


Common Mistakes

  • Calling the callback immediately instead of passing it

    (doSomething(callback()) instead of doSomething(callback))

  • Forgetting to handle error cases in asynchronous APIs

  • Creating nested callback chains instead of breaking logic into functions

  • Mixing callback styles with promise-based code inconsistently

  • Passing incorrect argument types (TypeScript helps avoid this)

Understanding how callbacks are passed and triggered prevents accidental bugs.


Summary

A callback is a function passed into another function so it can run later — usually in response to an event, a completed operation, or a condition being met. Callbacks make programs flexible, reusable, and capable of handling asynchronous tasks. They appear everywhere: JavaScript events, React components, Python sorting, Swift closures, and many other scenarios. Knowing how to use callbacks cleanly is essential for writing reliable modern software.

Learn to Code for Free
Start learning now
button icon
To advance beyond this tutorial and learn to code by doing, try the interactive experience of Mimo. Whether you're starting from scratch or brushing up your coding skills, Mimo helps you take your coding journey above and beyond.

Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.

Reach your coding goals faster