Understanding and using callback functions in JavaScript

In JavaScrip, function is a built-in class object, that is to say, it is a type of object that can be used for the management of built-in objects like other objects of String, Array, Number and Object classes. Because function is actually an object, it can be "stored in variables, passed through parameters to (another) function, created inside the function, and returned the result value from the function".

Because function is a built-in object, we can pass it as a parameter to another function, delay its execution to the function, or even return it after execution. This is the essence of using callback functions in JavaScript. The rest of this article will take a comprehensive look at JavaScript callback functions. Callback functions are probably the most widely used functional programming technology in JavaScript. Perhaps just a small piece of JavaScript or jQuery code will leave developers with a sense of mystery. Reading this article may help you eliminate that mystery.

Callback functions come from a well-known programming paradigm, functional programming. At the basic level, functional programming specifies the parameters of functions. Functional programming has been regarded as a difficult technology by "professional and smart" programmers, even though its scope of use has become smaller. It used to be and will be the same in the future.

Fortunately, functional programming has been elaborated that ordinary people like you and me can understand and use it. One of the most important techniques of functional programming is the callback function, which you will soon read is as simple as passing general parameter variables. The technology is so simple that I wonder why it is often included. JavaScript Go to high-level topics.

What are callbacks or advanced functions?

A callback function is considered to be a high-level function, a high-level function passed as a parameter to another function (in this case called "other function"), which is called (or executed) within the other function. The essence of callback function is a mode (a mode to solve common problems), so callback function is also called callback mode.

Consider the following callback functions commonly used in jQuery:

//Note that the item in the click method's parameter is a function, not a variable.
//The item is a callback function
$("#btn_1").click(function() {
  alert("Btn 1 Clicked");
});

As you can see in the previous example, we pass a function to the click method, which calls (or executes) the callback function we pass to it. This example gives a typical way to use callback functions in JavaScript and is widely used. jQuery Medium.

Take a closer look at another typical example of basic JavaScript:

var friends = ["Mike", "Stacy", "Andy", "Rick"];

friends.forEach(function (eachName, index){
console.log(index + 1 + ". " + eachName); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick
});

Again, we passed an anonymous function (a function without a function name) to the forEach method in the same way as a parameter to the forEach method.

So far, we have passed an anonymous function as a parameter to another function or method. Before looking at other more complex callback functions, let's understand how callbacks work and implement a callback function of our own.

How is the callback function implemented?

We can use a function as a parameter of another function, as a result of return in another function, and call it in another function as a variable. When we pass a callback function as a parameter to another function, we only pass the definition of the function and do not execute it in the parameter.

When the containing function has a callback function defined in the parameter, it can call it at any time.

This means that the callback function is not executed immediately, but "callback" it (as its name) at the specified position in the body of the function containing the function. So even if the first jQuery example looks like this:

//The anonymous function is not being executed there in the parameter. 
//The item is a callback function
$("#btn_1").click(function() {
  alert("Btn 1 Clicked");
});

Anonymous functions are delayed to be called in the body of the click function, and even if they have no name, they can be accessed by the included function through the arguments object.

Callback function is closure
When a callback function is passed as a parameter to another function, the callback function will be executed somewhere in the body of the containing function, just as the callback function is defined in the body of the containing function. This means that callback functions are closures. For more information about closures, please refer to another post by the author. Understand JavaScript Closures With Ease . As we all know, closure functions can access the scope of inclusion functions, so callback functions can access variables including functions, even global variables.

Basic Principles for Realizing Callback Functions

Simply put, there are several principles to follow when you implement callback functions yourself.

Use named or anonymous functions as callbacks
In the previous examples of jQuery and forEach, we defined anonymous functions in parameters containing functions. This is one of the general forms of using callback functions. Another frequently used form is to define a function with a name and pass the name of the function as a parameter to another function, for example:


// global variable
var allUserData = [];

// generic logStuff function that prints to console
function logStuff (userData) {
    if ( typeof userData === "string")
    {
        console.log(userData);
    }
    else if ( typeof userData === "object")
    {
        for (var item in userData) {
            console.log(item + ": " + userData[item]);
        }

    }

}

// A function that takes two parameters, the last one a callback function
function getInput (options, callback) {
    allUserData.push (options);
    callback (options);

}

// When we call the getInput function, we pass logStuff as a parameter.
// So logStuff will be the function that will called back (or executed) inside the getInput function
getInput ({name:"Rich", speciality:"JavaScript"}, logStuff);
//  name: Rich
// speciality: JavaScript

Transfer parameter to callback function
Because the callback function is executed as a general function, we can pass parameters to it. Any property (or global property) that contains a function can be passed as a parameter to the callback function. In the previous example, we passed the options containing the function as parameters to the callback function. The following example lets us pass a global or local variable to the callback function:

//Global variable
var generalLastName = "Clinton";

function getInput (options, callback) {
    allUserData.push (options);
// Pass the global variable generalLastName to the callback function
    callback (generalLastName, options);
}

Ensure that the callback is a function before execution
Before invoking, it is usually wise to ensure that callbacks passed in through parameters are a required function. In addition, it is also a good practice to make callback functions optional.

Let's refactor the getInput function in the example above to ensure that the callback function is properly checked.

function getInput(options, callback) {
    allUserData.push(options);

    // Make sure the callback is a function
    if (typeof callback === "function") {
    // Call it, since we have confirmed it is callable
        callback(options);
    }
}

If the getInput function is not properly checked (checking whether the callback is a function or passed in through parameters), our code will cause runtime errors.

The Problem of Using Callback Function with this Object
When a callback function is a method containing this object, we must modify the method of executing the callback function to protect the contents of this object. Otherwise, this object will point to the global window object (if the callback function is passed to the global function), or to the containing function. Let's look at the following code:

// Define an object with some properties and a method
// We will later pass the method as a callback function to another function
var clientData = {
    id: 094545,
    fullName: "Not Set",
    // setUserName is a method on the clientData object
    setUserName: function (firstName, lastName)  {
        // this refers to the fullName property in this object
      this.fullName = firstName + " " + lastName;
    }
}

function getUserInput(firstName, lastName, callback)  {
    // Do other stuff to validate firstName/lastName here

    // Now save the names
    callback (firstName, lastName);
}

In the following example code, when clientData.setUserName is executed, this.fullName does not set the attribute fullName in the clientData object, but sets the fullName in the window object, because getUserInput is a global function. This phenomenon occurs because this object points to the window object in the global function.

getUserInput ("Barack", "Obama", clientData.setUserName);

console.log (clientData.fullName);// Not Set

// The fullName property was initialized on the window object
console.log (window.fullName); // Barack Obama

Protect this object with Call or Apply functions

We can solve the problem in the previous example by using Call or Apply functions. So far, we know that every function in JavaScript has two methods: Call and Apply. These methods can be used to set the contents of this object inside the function and pass the contents to the object to which the function parameters point.

Call takes the value to be used as the this object inside the function as the first parameter, and the remaining arguments to be passed to the function are passed individually (separated by commas of course). The Apply function's first parameter is also the value to be used as the thisobject inside the function, while the last parameter is an array of values (or the arguments object) to pass to the function.

This sounds complicated, but let's see how easy it is to use Apply and Call. To solve the problems in the previous example, we use the Apply function as follows:

//Note that we have added an extra parameter for the callback object, called "callbackObj"
function getUserInput(firstName, lastName, callback, callbackObj)  {
    // Do other stuff to validate name here

    // The use of the Apply function below will set the this object to be callbackObj
    callback.apply (callbackObj, [firstName, lastName]);
}

By setting this object correctly with the Apply function, we can now execute the callback function correctly and set the fullName property in the clientData object correctly.

// We pass the clientData.setUserName method and the clientData object as parameters. The clientData object will be used by the Apply function to set the this object

getUserInput ("Barack", "Obama", clientData.setUserName, clientData);

// the fullName property on the clientData was correctly set
console.log (clientData.fullName); // Barack Obama

We can also use the Call function, but in this case we use the Apply function.

Multiple callback functions are also allowed
We can pass multiple callback functions to another function, just as we pass multiple variables. This is a typical example of using jQuery's AJAX function:

function successCallback() {
    // Do stuff before send
}

function successCallback() {
    // Do stuff if success message received
}

function completeCallback() {
    // Do stuff upon completion
}

function errorCallback() {
    // Do stuff if error received
}

$.ajax({
    url:"http://fiddle.jshell.net/favicon.png",
    success:successCallback,
    complete:completeCallback,
    error:errorCallback

});

The Problems and Solutions of "Callback Hell"

Asynchronous code execution is a simple way to execute in any order. Sometimes it's very common to have callback functions at many levels. You look like the code below. The messy code below is called "Callback Hell" because it's a troublesome code that contains a lot of callbacks. I'm at node.- MongoDB - native sees this example, MongoDB Driver Node.js The sample code looks like this:

var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory});
p_client.open(function(err, p_client) {
    p_client.dropDatabase(function(err, done) {
        p_client.createCollection('test_custom_key', function(err, collection) {
            collection.insert({'a':1}, function(err, docs) {
                collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) {
                    cursor.toArray(function(err, items) {
                        test.assertEquals(1, items.length);

                        // Let's close the db
                        p_client.close();
                    });
                });
            });
        });
    });
});

You are unlikely to encounter this problem in your code, but if you encounter it (or encounter it later), there are two ways to solve it.

  1. Name and define your function, then pass the function name as a callback instead of defining an anonymous function in the parameter list of the main function.
  2. Modularization: Divide your code into modules so that you can spare some code blocks for special work. Then you can introduce this model into your large applications.

    



Implementing your own callback function

Now you've fully understood (I'm sure you've understood, if you don't, reread it quickly) the features JavaScript uses for callbacks and see how simple but powerful callbacks are. You should see if your code has a chance to use callback functions. You can consider using callbacks when you have the following requirements:

  • Avoid duplicate code (DRY - Do Not Repeat Yourself)
  • Better abstraction where you need more general functionality (which can handle various types of functions).
  • Enhance code maintainability
  • Enhance code readability
  • More customized features

It's easy to implement my own callback function. In the following example, I can create a function to accomplish the work I used: get user data, use user data to generate a general poem, use user data to welcome users, but this function will be a messy function, full of if/else judgments, and even There are many limitations and other functions that the application may need to process user data.

Instead, I added a callback function to the implementation, so that the main function can pass the full name and gender of the user to the callback function parameters and perform the callback function to complete any task after obtaining user data.

In short, the getUserInput function is generic and can perform multiple callback functions with various functions.

// First, setup the generic poem creator function; it will be the callback function in the getUserInput function below.
function genericPoemMaker(name, gender) {
    console.log(name + " is finer than fine wine.");
    console.log("Altruistic and noble for the modern time.");
    console.log("Always admirably adorned with the latest style.");
    console.log("A " + gender + " of unfortunate tragedies who still manages a perpetual smile");
}

//The callback, which is the last item in the parameter, will be our genericPoemMaker function we defined above.
function getUserInput(firstName, lastName, gender, callback) {
    var fullName = firstName + " " + lastName;

    // Make sure the callback is a function
    if (typeof callback === "function") {
    // Execute the callback function and pass the parameters to it
    callback(fullName, gender);
    }
}

Call the getUserInput function and pass the genericPoemMaker function as a callback:

getUserInput("Michael", "Fassbender", "Man", genericPoemMaker);
// Output
/* Michael Fassbender is finer than fine wine.
Altruistic and noble for the modern time.
Always admirably adorned with the latest style.
A Man of unfortunate tragedies who still manages a perpetual smile.
*/

Because the getUserInput function only handles user data input, we can pass any callback function to it. For example, we can pass a greetUser function like this.

function greetUser(customerName, sex)  {
   var salutation  = sex && sex === "Man" ? "Mr." : "Ms.";
  console.log("Hello, " + salutation + " " + customerName);
}

// Pass the greetUser function as a callback to getUserInput
getUserInput("Bill", "Gates", "Man", greetUser);

// And this is the output
Hello, Mr. Bill Gates

As in the previous example, we called the same getUserInput function, but this time we performed a completely different task.

As you can see, callback functions provide a wide range of functions. Although the examples mentioned above are very simple, when you start using callback functions, think about how much work you can save and how you can better abstract Your code. Come on! Think about it when you wake up in the morning, before you go to bed at night, and when you rest...

When we use callback functions frequently in JavaScript, we should pay attention to the following points, especially in the current web application development, in third-party libraries and frameworks.

  • Asynchronous execution (such as reading files, sending HTTP requests)
  • Event monitoring and handling
  • Method of setting timeout and time interval
  • Universalization: Simple code

Keywords: Javascript Programming JQuery REST

Added by pmaiorana on Thu, 11 Jul 2019 01:35:34 +0300