Home JavaScript - Callback Function Example
Post
Cancel

JavaScript - Callback Function Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function findUser(id) {
  let user;
  setTimeout(function () {
    console.log("waited 0.1 sec.");
    user = {
      id: id,
      name: "User" + id,
      email: id + "@test.com",
    };
  }, 100);
  return user;
}

const user = findUser(1);
console.log("user:", user);

function findUserAndCallBack(id, cb) {
  setTimeout(function () {
    console.log("waited 0.1 sec.");
    const user = {
      id: id,
      name: "User" + id,
      email: id + "@test.com",
    };
    cb(user);
  }, 100);
}

findUserAndCallBack(1, function (user) {
  console.log("user:", user);
});

Alright, let’s break this down so those sneaky functions and callbacks stop feeling like a slap in the face. Your frustration is valid—this is one of the trickiest things to wrap your head around when dealing with JavaScript’s asynchronous nature. Let’s make it crystal clear.


What’s the Difference Between a Regular Function and a Callback Function?

  1. Regular Function:
    • A function that executes immediately when it is called and returns a value directly.
    • Works perfectly in synchronous code but falls apart in asynchronous situations.
  2. Callback Function:
    • A function passed as an argument to another function.
    • It’s executed later, typically after some asynchronous operation (like setTimeout, database query, or an API call) is complete.

Code Walkthrough

Example 1: Regular Function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function findUser(id) {
  let user;
  setTimeout(function () {
    console.log("waited 0.1 sec.");
    user = {
      id: id,
      name: "User" + id,
      email: id + "@test.com",
    };
  }, 100);
  return user;
}

const user = findUser(1);
console.log("user:", user);


What Happens Here?

  1. findUser(1) is called:
    • A setTimeout is initiated, scheduling a delay of 0.1 seconds (100ms).
    • The inner function inside setTimeout logs "waited 0.1 sec." and assigns the user object after 100ms.
  2. return user happens immediately after setTimeout is scheduled:
    • At this point, user is undefined because setTimeout hasn’t executed yet.
    • JavaScript does not “pause” and wait for the setTimeout to finish.
  3. Output:
    • "user: undefined" is logged because the user object wasn’t ready when the function returned.

Why Does It Fail?

  • The function tries to return a value (user) before the asynchronous code (setTimeout) finishes running.
  • In JavaScript, asynchronous code (like setTimeout) doesn’t block the execution of subsequent code.

Example 2: Using a Callback Function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function findUserAndCallBack(id, cb) {
  setTimeout(function () {
    console.log("waited 0.1 sec.");
    const user = {
      id: id,
      name: "User" + id,
      email: id + "@test.com",
    };
    cb(user); // Call the callback with the user object
  }, 100);
}

findUserAndCallBack(1, function (user) {
  console.log("user:", user);
});


What Happens Here?

  1. findUserAndCallBack(1, callbackFunction) is called:
    • A setTimeout is initiated, scheduling a delay of 0.1 seconds (100ms).
    • After 100ms, the inner function executes, creating the user object.
  2. Callback Function (cb(user)):
    • The user object is passed to the callback function (function(user) { console.log("user:", user); }).
    • This callback is invoked after the setTimeout finishes, ensuring the user object is ready.
  3. Output:
    • "waited 0.1 sec." (from the inner setTimeout function).
    • "user: { id: 1, name: 'User1', email: '1@test.com' }" (from the callback).

Why Does It Work?

  • Instead of trying to return the value directly, the findUserAndCallBack function uses a callback to deliver the result when it’s ready.
  • The callback waits for the asynchronous operation to complete, ensuring the data is available before it is used.

Key Difference

AspectRegular FunctionCallback Function
Return TimingReturns immediately, even if the result isn’t ready.Executes later, only when the result is ready.
Asynchronous HandlingFails to handle async operations properly.Works with async operations by waiting for completion.
Use CaseUse when all data is available synchronously.Use when dealing with async tasks like setTimeout or API calls.

Simplified Analogy

Imagine calling a pizza delivery service:

  • Regular Function: You call and expect the pizza immediately. If the pizza isn’t ready (which it isn’t), you hang up and leave hungry.
  • Callback Function: You call and ask them to let you know when the pizza is ready. They call you back after it’s done, and you’re happy with your hot, fresh pizza.

Real Practice Tip

Callbacks are common, but managing them for complex workflows can lead to callback hell. To make your life easier, look into Promises or async/await, which streamline handling asynchronous operations.

Want to level up with Promises or async/await? Just let me know—I’ve got you covered! 😉


2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// in this code,

function findUserAndCallBack(id, cb) {
  setTimeout(function () {
    console.log("waited 0.1 sec.");
    const user = {
      id: id,
      name: "User" + id,
      email: id + "@test.com",
    };
    cb(user); // 1
  }, 100); // 2
}

findUserAndCallBack(1, function (user) {
  console.log("user:", user);
});

// does //2 certainly start after //1?

No, //2** does not wait for **//1** to finish.** The execution of the code inside the setTimeout block is asynchronous, meaning //2 (the timer delay) begins immediately when setTimeout is called, but the actual callback function (inside setTimeout) will only execute after 100 milliseconds, independently of the flow outside.

Let’s break it down step by step:


How **setTimeout** Works in JavaScript

  1. setTimeout** schedules a task to be run later**:
    • The code inside the setTimeout block is added to the JavaScript event queue.
    • It won’t execute until the current execution context (all synchronous code) is finished and at least 100ms have passed.
  2. The callback (cb(user)):
    • It is executed after 100ms, assuming the JavaScript thread is free. It doesn’t block the rest of the code outside the setTimeout.

Execution Order in Your Code

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function findUserAndCallBack(id, cb) {
  setTimeout(function () { // 2
    console.log("waited 0.1 sec.");
    const user = {
      id: id,
      name: "User" + id,
      email: id + "@test.com",
    };
    cb(user); // 1
  }, 100);
}

findUserAndCallBack(1, function (user) {
  console.log("user:", user);
});

Step-by-Step Execution:

  1. Call **findUserAndCallBack**:
    • The function is called with id = 1 and a callback function.
  2. setTimeout** is scheduled** (//2):
    • A timer for 100 milliseconds is started.
    • The callback function inside the setTimeout block is queued to run after 100ms.
    • No blocking occurs, so the program can continue executing.
  3. After 100ms (timer expires):
    • The JavaScript engine executes the callback function inside setTimeout.
    • "waited 0.1 sec." is logged.
    • The user object is created and passed to cb(user) (//1).
    • The provided callback function (function (user) { console.log("user:", user); }) runs and logs the user.

Key Points

  • //2** does not wait for **//1:

    The timer (100ms) starts immediately when setTimeout is called. However, the execution of the callback function (and //1) happens after the timer expires.

  • Event Loop Behavior:

    The setTimeout callback runs only after:

    1. The timer expires.
    2. The current call stack is clear (i.e., all synchronous code has finished).

Output

Given your code, the order of execution is:

  1. Timer starts (setTimeout schedules the task for 100ms).
  2. After 100ms, "waited 0.1 sec." is logged.
  3. cb(user) runs, and "user: { id: 1, name: 'User1', email: '1@test.com' }" is logged.

Timeline Visualization

```plain text Time: 0ms –> setTimeout schedules callback (timer starts ticking) Time: 100ms –> setTimeout callback executes –> “waited 0.1 sec.” logs –> cb(user) executes, logging the user object

```


So, to reiterate:

//2** does not depend on **//1. The timer starts immediately, and **//1** executes only after the timer expires. Let me know if you’d like a deeper dive into the event loop mechanics! 😊

This post is licensed under CC BY 4.0 by the author.