📖 JavaScript Callbacks

A callback is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of action.

Asynchronous Callbacks

Asynchronous callbacks are used to continue code execution after an asynchronous operation has completed. This is crucial for operations like event handling, timers, and API calls.

What is an API?

An API (Application Programming Interface) is a set of rules that allows different software entities to communicate with each other. In web development, APIs often refer to web services that provide data or functionality that can be accessed over the internet.

Example: setTimeout


function greet() {
    console.log('Hello, world!');
}
setTimeout(greet, 2000); // 'Hello, world!' will be logged after 2 seconds

In this example, the greet function is passed as a callback to the setTimeout function. It will be executed after 2 seconds, making it a callback function.

Example: Event Listener


document.getElementById('btn').addEventListener('click', function() {
    console.log('Button was clicked!');
});

In this example, an anonymous function is passed as a callback to the addEventListener method. It will be executed whenever the specified event (click) occurs on the element, making it a callback function.

Position of Callback Functions

The position of the callback function in the argument list depends on the specific function or method:

  • setTimeout: The callback is the first argument, followed by the delay in milliseconds.
  • addEventListener: The event type is the first argument, and the callback function is the second argument.

Example: API Call with Callbacks

Here's an example of how an API call can be made using callbacks. In this example, we simulate an API call with a timeout function.


function fetchData(callback) {
    setTimeout(() => {
        const data = { name: 'John', age: 30 };
        callback(data);
    }, 2000);
}

function displayData(data) {
    console.log(`Name: ${data.name}, Age: ${data.age}`);
}

fetchData(displayData); // 'Name: John, Age: 30' will be logged after 2 seconds

In this example:

  • The fetchData function simulates an API call by using setTimeout to delay the execution of its inner code.
  • Inside setTimeout, after 2 seconds, an object data is created with some dummy information.
  • The callback function, which is passed as an argument to fetchData, is called with data as its argument.
  • The displayData function is defined to log the data to the console.
  • When fetchData is called with displayData as its argument, after 2 seconds, displayData is invoked with the dummy data, and it logs 'Name: John, Age: 30' to the console.

Putting It Into Action

To see these examples in action, create an HTML file and include the following script. This script will demonstrate the core principles of asynchronous callbacks and log the results to the console.


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Asynchronous Callbacks Example</title>
</head>
<body>
    <button id="btn">Click Me>/button>
    <script>
        // Example: setTimeout
        function greet() {
            console.log('Hello, world!');
        }
        setTimeout(greet, 2000); // 'Hello, world!' will be logged after 2 seconds

        // Example: Event Listener
        document.getElementById('btn').addEventListener('click', function() {
            console.log('Button was clicked!');
        });

        // Example: API Call with Callbacks
        function fetchData(callback) {
            setTimeout(() => {
                const data = { name: 'John', age: 30 };
                callback(data);
            }, 2000);
        }

        function displayData(data) {
            console.log(`Name: ${data.name}, Age: ${data.age}`);
        }

        fetchData(displayData); // 'Name: John, Age: 30' will be logged after 2 seconds
    </script>
</body>
</html>

Challenge

This challenge builds on the "Putting It Into Action" example. Modify the API call example to simulate a failure. Use a second callback to handle errors and log an error message to the console.

In order to check your learning, you should attempt to create a solution before revealing the provided solution below.


function fetchData(successCallback, errorCallback) {
    setTimeout(() => {
        const success = Math.random() > 0.5; // Randomly succeed or fail
        if (success) {
            const data = { name: 'John', age: 30 };
            successCallback(data);
        } else {
            errorCallback('Failed to fetch data.');
        }
    }, 2000);
}

function displayData(data) {
    console.log(`Name: ${data.name}, Age: ${data.age}`);
}

function handleError(error) {
    console.error(error);
}

fetchData(displayData, handleError); // Will randomly log success or error after 2 seconds

Issues with Callbacks

Using callbacks for asynchronous operations can lead to complex and hard-to-read code, known as "callback hell". To address these issues, JavaScript introduced promises and async/await.

In the next sections, we will explore promises and async/await, which provide more elegant solutions for handling asynchronous code.

References