Callback in java script | Java Script Interview Series

👉 A function that is passed as an argument to another function.

Note :

  • The function is then executed sometimes later inside the body.
  • Everything in JavaScript are object, and we can pass objects inside an function as parameter .

 

Why do we need callback

♦️ JavaScript runs code sequentially in top-down order. However, there are some cases that code runs (or must run) after something else happens and also not sequentially. This is called asynchronous programming.

♦️ Callbacks helps you control the order of execution. It makes sure that a function is not going to run before a task is completed but will run right after the task has completed. It helps us develop asynchronous JavaScript code and keeps us safe from problems and errors.

How to create an callback function

  1. Creating callback as function expression
function greetUser(name, callback) {
  console.log("Hi " + name);
  callback();
}

function sayBye() {
  console.log("Bye!");
}

greetUser("Rahul", sayBye);

 

Here the message function is being called after 3 second meaning we are holding the function for some time and then calling , hence this is one of the advantage of callback functions

 

  1. Creating as Anonymous Function (with setTimeOut)
setTimeout(function() {  
    console.log("This message is shown after 3 seconds");
}, 3000);

 

 

  1. Callback as an Arrow Function
setTimeout(() => { 
    console.log("This message is shown after 3 seconds");
}, 3000);

 

All 3 will do the same job

 

Callback helps in asynchronous programming

👉 Callback help in executing a code only after a following response and so on.

Here the function will wait for 3 seconds to print , hence we are able to control the execution according to our need.

const printMeAfter3sec = () => {
  console.log("hello");
}

setTimeout(printMeAfter3sec, 3000);

 

Let’s understand how callback works:

console.log("Loading...")

function fetchDataCallback(callback) {
  console.log("Fetching data...")
  
  setTimeout(() => {
    const data = { id: 1, name: "John Doe" } // Simulating fetched data
    callback(data) // Call the callback with the data after fetching
  }, 2000) // Simulate a 2-second delay
}

// Using the function with a callback
fetchDataCallback((result) => {
  console.log("Data fetched:", result)
})

console.log("End...")

 

Explanation

Diagram explaining how its work behind the scene

1. Global Execution Context

When JavaScript starts executing this code, it first creates a Global Execution Context. This includes all the variables, function declarations, and any code that needs to run at the top level.

  • The function fetchDataCallback is defined and stored in memory.
  • The fetchDataCallback function isn’t executed yet, just defined.

2. Function Invocation (Synchronous Part)

fetchDataCallback((result) => {
    console.log('Data fetched:', result);
});

 

  • Now, fetchDataCallback is invoked with a callback function passed as an argument.
  • A new execution context is created for this function call, where the callback parameter is assigned the anonymous function (result) => { console.log('Data fetched:', result); }.

3. Execution of console.log('Fetching data...')

Inside fetchDataCallback, the first statement is:

console.log('Fetching data...');

 

  • This is a synchronous operation, so it immediately logs "Fetching data…" to the console.

4. Encountering setTimeout

Next, we encounter this code:

setTimeout(() => {
    const data = { id: 1, name: 'John Doe' };
    callback(data);
}, 2000);

 

  • setTimeout is a Web API provided by the browser (or Node.js in server environments).
  • It takes two arguments:
    1. A callback function (which we want to execute after the delay).
    2. A delay (2000 milliseconds or 2 seconds).
  • When JavaScript encounters setTimeout, it offloads this task to the Web API environment (outside of the JavaScript engine).

Important: The Web API (in this case, setTimeout) handles the timer in the background while the main JavaScript thread continues executing other code.

5. Finishing the Synchronous Code

After setTimeout is sent to the Web API, the synchronous code inside fetchDataCallback finishes execution. At this point, the JavaScript call stack is empty because there are no more statements left to execute.

6. Timer and Web APIs (Asynchronous Part)

  • While the main thread is free, the Web API timer runs in the background for 2 seconds (2000ms).
  • After 2 seconds, the timer expires, and the callback function inside setTimeout (() => { const data = { id: 1, name: 'John Doe' }; callback(data); }) is sent to the task queue.

7. Event Loop

  • The event loop continuously monitors the call stack and the task queue.
  • Once the call stack is empty (which it is after the synchronous code finishes), the event loop pushes tasks from the task queue to the call stack.

8. Callback Execution (Async Code)

After 2 seconds, the event loop pushes the callback function inside setTimeout to the call stack:

() => {
    const data = { id: 1, name: 'John Doe' };
    callback(data);
}

 

  • Inside this function:
    1. The data object is created: { id: 1, name: 'John Doe' }.
    2. The callback function is invoked with this data object as an argument.

9. Executing the Passed Callback

Now, the callback (result) => { console.log('Data fetched:', result); } is called with the data as result.

  • A new execution context is created for this callback.
  • The statement console.log('Data fetched:', result) is executed.
  • It logs the final result: Data fetched: { id: 1, name: 'John Doe' } to the console.

Visual Timeline:

  1. Start: Function fetchDataCallback is called → logs "Fetching data…"
  2. setTimeout: The setTimeout function is offloaded to Web APIs → 2-second timer starts.
  3. Synchronous Code Ends: Main thread becomes idle → waits for async tasks.
  4. After 2 Seconds: Timer expires → callback is pushed to task queue → event loop moves it to the call stack.
  5. Callback Executes: Logs the final data.

Summary of Web APIs Role:

  • The Web API (setTimeout) is responsible for the timer.
  • The event loop ensures that after the timer expires, the callback is executed asynchronously once the call stack is clear.
  • Callbacks allow us to handle asynchronous operations like these, where some operations (like waiting for data) are handled by the browser or Node.js outside of the JavaScript execution engine.

 

 

Let’s understand this with one more problem statement –

In the below code . the output of message is coming as “undefined” .

 

console.log("hello");

function asyncMe(name) {
  setTimeout(function () {
    return `${name}`;
  }, 1000);
}

const message = asyncMe("himanshu");
console.log(message);  
console.log("bye");

 

Reason → The message o/p will come undefined , because asyncMe() is an asynchronous function meaning it will execute at last.

 

To fix the above code, we can use callback

Just define a functions inside a parameter (i.e callback) , and pass it inside setTimeout() , hence solved

console.log("hello");

function asyncMe(name, callback) {
  setTimeout(function () {
    const message = `print to ${name}`;
    callback(message); // Call the callback function with the message
  }, 1000);
}

asyncMe("himanshu", function(message) {
  console.log(message); // This will be executed after the timeout
});

console.log("bye");

 

 

In coming lectures, we will see the other ways of fixing the above code using promise and async await

 

Question : Fix this with callback

function getUserData(id) {
    setTimeout(() => {
        if (!id) {
            throw new Error("User ID is required!");
        }
        return { id, name: "John Doe" }; // This return won't work properly in async code
    }, 1000);
}

const user = getUserData(1);
console.log(user); // This will be undefined

 

Solution :
function getUserData(id, callback) {
    setTimeout(() => {
        if (!id) {
            return callback(new Error("User ID is required!"), null);
        }
        const user = { id, name: "John Doe" };
        callback(null, user);
    }, 1000);
}

// Using the function with a callback
getUserData(1, (error, user) => {
    if (error) {
        console.error("Error:", error.message);
    } else {
        console.log("User Data:", user);
    }
});

// Testing the error case
getUserData(null, (error, user) => {
    if (error) {
        console.error("Error:", error.message);
    } else {
        console.log("User Data:", user);
    }
});

 

Other uses of callback

Callback helps in calling an event

//html
<button id="callback-btn">Click here</button>

//java script
document.queryselector("#callback-btn")
    .addEventListener("click", function() {    
      console.log("User has clicked on the button!");
});

 

The first one is its type, “click”, and the second parameter is a callback function, which logs the message when the button is clicked.

 

Callback function helps in creating a polyfill

For example, let create a polyfill for for each

let arr=  [2,4,5,3,5 ];

const myForEach = (arr,cb) => {
for(let i=0;i<arr.length;i++){
	const element = arr[i];
	cb(element)
}
}

myForEach(arr , (name)=> console.log(name));

 

🏠 Home work – Try creating polyfill for map, filter and reduce. Link

 

Help Others

Leave a Reply

Your email address will not be published. Required fields are marked *