Understanding Asynchronous Flow
Callbacks, Promises, and Futures



Introduction to Asynchronous Programming Flow

In synchronous programming, each line of code waits for the previous one to finish. But in the real world, not all tasks complete instantly — for example, reading a file or fetching data from the internet. These operations can delay the entire program. To avoid that, we use asynchronous programming, which allows non-blocking execution.

Callbacks

A callback is a function passed as an argument to another function, to be executed later. This is the most basic approach to handle asynchronous operations.

// Simulating an asynchronous operation using a callback
function fetchData(callback):
    print("Fetching data... (simulated delay)")
    wait 2 seconds
    data = "result"
    callback(data)

function handleData(result):
    print("Received:", result)

fetchData(handleData)

Output:

Fetching data... (simulated delay)
Received: result

Why use callbacks?

Callbacks allow us to run code *after* something finishes without blocking the flow of the entire program. But they can lead to messy code when nested too deeply — this is known as “callback hell.”

Question:

What happens if the callback is not invoked inside the async function?

Answer: The subsequent operation relying on the result never runs, leading to incomplete logic execution or silent failures.

Promises

A Promise is a construct that represents a value that might be available now, in the future, or never. It has three states: pending, fulfilled, or rejected.

// Simulating a Promise-like behavior
function getPromise():
    promise = createPromise()
    wait 2 seconds
    if success:
        promise.resolve("Success Data")
    else:
        promise.reject("Error Occurred")
    return promise

promise = getPromise()
promise.then(handleSuccess)
       .catch(handleFailure)

function handleSuccess(data):
    print("Data received:", data)

function handleFailure(error):
    print("Something went wrong:", error)

Output:

Data received: Success Data

Promises make the code more readable and manageable than callbacks by chaining responses using then() and handling errors with catch().

Question:

What if you don’t call catch() on a rejected promise?

Answer: The error goes unhandled, potentially crashing your application or leading to hard-to-find bugs.

Futures

A Future is a placeholder for a value that will be computed later, commonly seen in multi-threaded or concurrent environments. While similar to Promises, Futures are more common in typed or compiled languages and are tied closely with execution threads.

// Simulating a future
function computeHeavyTask():
    future = createFuture()
    runInBackground(() -> {
        wait 3 seconds
        result = 42
        future.setResult(result)
    })
    return future

future = computeHeavyTask()
value = future.get() // This waits until result is ready
print("Computed value:", value)

Output:

Computed value: 42

Question:

How is a Future different from a Promise?

Answer: While both represent a value that becomes available later, Futures are often blocking when accessed (e.g., get() waits), whereas Promises typically use non-blocking handlers like then().

Summary

When to Use What?



Welcome to ProgramGuru

Sign up to start your journey with us

Support ProgramGuru.org

Mention your name, and programguru.org in the message. Your name shall be displayed in the sponsers list.

PayPal

UPI

PhonePe QR

MALLIKARJUNA M