# Callbacks in JavaScript: Why They Exist

## The Flow of CallBack

To understand callbacks, first, we have to know about functions.

Functions are **non-primitive data types** in JavaScript. But, functions are **First-Class Citizens**. This means a function is just another piece of data, like a string or a number. You can:

*   Store a function in a **variable**.
    
*   Pass a function as an **argument** to another function.
    
*   Return a function from another function.
    

## Store a function in a **variable**

```javascript
// 1. Store a function in a variable (The Recipe)
const makePasta = function() {
  console.log("Cooking delicious pasta... 🍝");
};

makePasta()
```

Output:

```javascript
"Cooking delicious pasta... 🍝"
```

We store the function in a variable when we want to execute the function after declaration. In Js there is a concept of Hoisting. Function hoist the whole body and variable hoists the reference. To know more read the MDN docs.

## Pass a function as an **argument** to another function

```javascript
// 2. Pass a function as an argument (The Sous-Chef)
// 'callback' is the recipe we hand to the chef
function chef(customerName, callback) {
  console.log("Chef is preparing the order for " + customerName);
  callback(); // Calling the function we passed in
}
// CALLING IT: We pass the 'makePasta' recipe to the chef
chef("Alice", makePasta);
```

Output:

```javascript
Chef is preparing the order for Alice
Cooking delicious pasta... 🍝
```

### **The Execution Flow**

1.  **Definition Phase**: The browser reads your code and remembers two things:
    
    *   There is a "recipe" called `makePasta`.
        
    *   There is a "worker" called `chef` who expects a name and a set of instructions.
        
2.  **The Call (**`chef("Alice", makePasta)`**)**:
    
    *   The program jumps into the `chef` function.
        
    *   It assigns `"Alice"` to the variable `customerName`.
        
    *   It assigns the **entire** `makePasta` **function** to the variable `callback`. (Crucial: It hasn't "cooked" yet; it's just holding the recipe).
        
3.  **Inside the Chef**:
    
    *   **Line 1:** `console.log` runs immediately. Output: *"Chef is preparing the order for Alice"*.
        
    *   **Line 2 (**`callback()`**):** The Chef looks at the recipe paper (`callback`) and sees the `()` "action" symbols. This triggers the code inside `makePasta`.
        
4.  **The Callback Execution**:
    
    *   The program jumps *out* of the chef’s main logic and *into* the `makePasta` block.
        
    *   **Output:** *"Cooking delicious pasta... 🍝"*.
        
5.  **Completion**:
    
    *   `makePasta` finishes.
        
    *   The `chef` function finishes.
        
    *   The program moves to the next line of code after the initial call
        

MAIN PROGRAM

```plaintext
|
|-- calls chef("Alice", makePasta)
      |

      |--> [Inside chef]
      |    1. Logs "Chef is preparing..."
      |    2. Sees callback() -------------> [Jumps to makePasta]

      |                                       1. Logs "Cooking delicious..."
      |                                       2. Finishes
      | <------------------------------------ [Returns to chef]

      |
      |--> [chef finishes]
```

## Return a function from another function

```javascript
// 3. Return a function from another function
function getRecipeGenerator(style) {
  if (style === "spicy") {
    return function() { console.log("Adding chili flakes... 🌶️"); };
  } else {
    return function() { console.log("Adding extra cheese... 🧀"); };
  }
}

// CALLING IT:
// First, we get the specific recipe (the returned function)
const mySecretRecipe = getRecipeGenerator("spicy");

// Then, we actually execute that recipe whenever we want
mySecretRecipe(); 
```

Output:

```javascript
Adding chili flakes... 🌶️
```

### **The Execution Flow**

1.  **Definition Phase**:
    
    *   The browser stores the `getRecipeGenerator` logic. It knows this function is a "Generator"—its only job is to hand back a smaller, specific function.
        
2.  **The First Call (**`const mySecretRecipe = getRecipeGenerator("spicy")`**)**:
    
    *   The program enters `getRecipeGenerator` with the input `"spicy"`.
        
    *   It checks the `if` statement: Is it spicy? **Yes**.
        
    *   It hits the `return` keyword. Instead of returning a number or string, it returns the **entire block of code** inside that `function() { ... }`.
        
    *   The variable `mySecretRecipe` now **is** that function. It is sitting in your memory, waiting to be triggered.
        
    *   *Note: Nothing has been logged to the console yet.*
        
3.  **The Second Call (**`mySecretRecipe()`**)**:
    
    *   This is the "Action" phase. You use the `()` parentheses on your new variable.
        
    *   The program jumps into the code that was returned in Step 2.
        
    *   **Output:** *"Adding chili flakes... 🌶️"*.
        
4.  **The Completion**:
    
    *   The code inside the returned function finishes.
        
    *   The program moves on.
        

```plaintext
STEP 1: The Request
[ Main Code ] --- "Give me a spicy recipe" ---> [ getRecipeGenerator ]

                                                       |
                                            (Checks "spicy" logic)
                                                       |
[ Main Code ] <--- [ Returns Function Block ] <---------

STEP 2: The Storage
mySecretRecipe = function() { console.log("Adding chili flakes... 🌶️"); }

STEP 3: The Execution
mySecretRecipe()  --->  [ Executes the stored block ]
                        Output: "Adding chili flakes... 🌶️"
```

## **What is a Callback?**

In JavaScript, functions are **First-Class Citizens**. This means they can be treated like any other variable: you can assign them to values, return them from other functions, and—most importantly—pass them as **arguments** to other functions.

A **callback** is simply a function passed into another function with the expectation that it will be executed (called back) at a later time.

## **Why Callbacks for Async Programming?**

JavaScript is **single-threaded**, meaning it can only do one thing at a time. If you ask it to fetch data from a server, the whole browser would freeze while waiting for the response.

Callbacks solve this by saying: *"Go start this long task. I’m going to keep running other code. When you’re finished, run this specific callback function."*

**Common Scenarios:**

*   **Event Listeners:** `button.addEventListener('click', () => { ... })`
    
*   **Timers:** `setTimeout(doSomething, 2000)`
    
*   **Data Fetching:** Handling the result of a database query or API call.
    

## **The Conceptual Flow**

1.  **Function A** is called with **Function B** (the callback) as an argument.
    
2.  **Function A** starts an asynchronous task (like a timer).
    
3.  The main program continues running.
    
4.  The task finishes, and **Function B** is pushed onto the execution stack to run.
    

![](https://cdn.hashnode.com/uploads/covers/69692b510af0edc063a3ba8c/4e06a589-7101-4c9f-a5fa-b90df1d79bf4.png align="center")

## **The Problem: Callback Hell**

When you have multiple asynchronous tasks that depend on each other, you end up nesting callbacks inside callbacks. This creates a "pyramid" shape in your code that is incredibly hard to read and debug.

```javascript
getData(function(a) {
  getMoreData(a, function(b) {
    getEvenMoreData(b, function(c) {
      console.log(c); // This is "Callback Hell"
    });
  });
});
```

The biggest problem with callbacks is **nesting**. If you have three tasks that must happen one after the other (Step A 👉Step B👉Step C), your code starts to crawl sideways.

**Conceptually, it looks like this:**

*   Do Task 1...
    
    *   ...once Task 1 is done, do Task 2...
        
        *   ...once Task 2 is done, do Task 3...
            
            *   ...once Task 3 is done, finally finish.
                

This creates a "Pyramid of Doom." It’s hard to read, even harder to handle errors (where did it break?), and makes the code very brittle.

## **📝 Assignment: The "Custom Coffee" Challenge**

To wrap up, try this exercise to see if you’ve mastered the flow of functions as values and callbacks.

**The Scenario:** You are building a **Coffee Shop Simulator**.

1.  **Step 1:** Create a function called `makeCoffee`. It should take two arguments: `type` (a string) and `onDone` (a callback function).
    
2.  **Step 2:** Inside `makeCoffee`, log: *"Brewing your \[type\]... Please wait."*
    
3.  **Step 3:** Use `onDone()` to signal the coffee is ready.
    
4.  **Step 4:** Define a separate function called `alertCustomer`. It should log: *"Your order is ready! ☕"*
    
5.  **Step 5 (The Execution):** Call `makeCoffee`, passing in "Latte" and the `alertCustomer` function.
    

**Bonus Goal:** Can you wrap `onDone()` in a `setTimeout` so the alert only happens after 3 seconds?

## **🏁 Conclusion**

Callbacks might feel like "code inception" at first, but they are the secret sauce that makes JavaScript fast and interactive. By treating functions as **values**—things you can store, pass around, and return—you gain the power to:

*   **Wait without freezing:** Your app stays responsive while heavy tasks happen in the background.
    
*   **Write cleaner logic:** You can separate "what to do" (the recipe) from "when to do it" (the chef).
    
*   **Create dynamic code:** You can build "function factories" that generate custom logic on the fly.
    

Happy coding! 💕
