
Understanding callback functions (callbacks)
- Transfer
Callbacks are extremely important in Javascript. They are almost everywhere. But, despite the experience of programming in C / Java, I had difficulties with them (as well as with the very idea of asynchronous programming), and I decided to figure it out. Strange, but I couldn’t find good introductory articles about callback functions on the Internet - basically there were snippets of documentation on functions
To understand callback functions, you need to understand ordinary functions. It may seem commonplace, but functions in Javascript are a bit strange stuff.
Functions in Javascript are actually objects. Namely, class objects
The advantage of the function-as-object concept is that code can be passed to another function in the same way as a regular variable or object (because in the literal sense, code is just an object).
Passing a function as an argument is simple.
It may seem stupid to create such a sophisticated code when you can return the value in the normal way, but there are situations in which it is impractical and callback functions are necessary.
Traditionally, functions take arguments as input and return using an expression
Javascript makes it possible to do things a little differently. Instead of waiting for the function to finish executing and returning the value, we can use callback functions to get it asynchronously. This is useful for cases when it takes a long time to complete, for example, with AJAX requests, because we can not pause the browser. We can continue to do other things while waiting for the callback to be called. In fact, very often we are required (or rather, we strongly recommend) to do everything asynchronously in Javascript.
Here is a more detailed example, which uses AJAX to load an XML file and uses a function
In this example, we create the httpRequest object and load the XML file. The typical paradigm of returning a value at the end of a function no longer works here. Our request is processed asynchronously, which means that we start the request and tell it to call our function as soon as it ends.
We use two anonymous functions here. It is important to remember that it would not be difficult for us to use named functions, but for the sake of brevity we made them nested. The first anonymous function is executed whenever a status changes in our httpRequest object. We ignore this until the status is 4 (i.e., the request is completed) and the status is 200 (i.e., the request is successful). In the real world, you would like to check if the request failed, but we assume that the file exists and can be downloaded by the browser. This anonymous function is associated with httpRequest.onreadystatechange, so it is not executed immediately, but is called every time a state changes in our request.
When we finally complete our AJAX request, we do not just run the callback function, we use the function for this
It’s
Finally, the second console.log expression will be executed first, because the callback function is not executed until the request is completed, and until this happens, the subsequent parts of the code continue to execute quietly.
I hope you now understand callbacks well enough to start using them in your own code. It’s still difficult for me to structure the code that is based on callback functions (in the end it becomes like spaghetti ... my mind is too used to ordinary structural programming), but they are a very powerful tool and one of the most interesting parts of the Javascript language.
call()
and apply()
or short pieces of code demonstrating their use, and now, having filled the bumps in search of truth, I decided to write an introduction to callback- functions on their own.Functions are objects
To understand callback functions, you need to understand ordinary functions. It may seem commonplace, but functions in Javascript are a bit strange stuff.
Functions in Javascript are actually objects. Namely, class objects
Function
created by the constructor Function
. The object Function
contains a string with the JS code of this function. If you switched from C or Java, it may seem strange (how can the code be a string ?!), but, generally speaking, in Javascript it's all the time. The distinction between code and data is sometimes blurred.// можно создать функцию, передав в конструктор Function строку с кодом
var func_multiply = new Function("arg1", "arg2", "return arg1 * arg2;");
func_multiply(5, 10); // => 50
The advantage of the function-as-object concept is that code can be passed to another function in the same way as a regular variable or object (because in the literal sense, code is just an object).
Passing a function as a callback function
Passing a function as an argument is simple.
// определяем нашу функцию с аргументом callback
function some_function(arg1, arg2, callback) {
// переменная, генерирующая случайное число в интервале между arg1 и arg2
var my_number = Math.ceil(Math.random() * (arg1 - arg2) + arg2);
// теперь всё готово и мы вызываем callback, куда передаём наш результат
callback(my_number);
}
// вызываем функцию
some_function(5, 15, function (num) {
// эта анонимная функция выполнится после вызова callback-функции
console.log("callback called! " + num);
});
It may seem stupid to create such a sophisticated code when you can return the value in the normal way, but there are situations in which it is impractical and callback functions are necessary.
Do not clutter the exit
Traditionally, functions take arguments as input and return using an expression
return
(ideally, the only expression return
at the end of the function is one entry point and one exit point). It makes sense. Functions are essentially routes between input and output.Javascript makes it possible to do things a little differently. Instead of waiting for the function to finish executing and returning the value, we can use callback functions to get it asynchronously. This is useful for cases when it takes a long time to complete, for example, with AJAX requests, because we can not pause the browser. We can continue to do other things while waiting for the callback to be called. In fact, very often we are required (or rather, we strongly recommend) to do everything asynchronously in Javascript.
Here is a more detailed example, which uses AJAX to load an XML file and uses a function
call()
to call a callback function in the context of the requested object (this means that when we specify the keywordthis
inside a callback function, it will refer to the requested object):function some_function2(url, callback) {
var httpRequest; // создаём наш XMLHttpRequest-объект
if (window.XMLHttpRequest) {
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) {
// для дурацкого Internet Explorer'а
httpRequest = new
ActiveXObject("Microsoft.XMLHTTP");
}
httpRequest.onreadystatechange = function () {
// встраиваем функцию проверки статуса нашего запроса
// это вызывается при каждом изменении статуса
if (httpRequest.readyState === 4 && httpRequest.status === 200) {
callback.call(httpRequest.responseXML); // вызываем колбек
}
};
httpRequest.open('GET', url);
httpRequest.send();
}
// вызываем функцию
some_function2("text.xml", function () {
console.log(this);
});
console.log("это выполнится до вышеуказанного колбека");
In this example, we create the httpRequest object and load the XML file. The typical paradigm of returning a value at the end of a function no longer works here. Our request is processed asynchronously, which means that we start the request and tell it to call our function as soon as it ends.
We use two anonymous functions here. It is important to remember that it would not be difficult for us to use named functions, but for the sake of brevity we made them nested. The first anonymous function is executed whenever a status changes in our httpRequest object. We ignore this until the status is 4 (i.e., the request is completed) and the status is 200 (i.e., the request is successful). In the real world, you would like to check if the request failed, but we assume that the file exists and can be downloaded by the browser. This anonymous function is associated with httpRequest.onreadystatechange, so it is not executed immediately, but is called every time a state changes in our request.
When we finally complete our AJAX request, we do not just run the callback function, we use the function for this
call()
. This is another way to call a callback function. The method we used before - just starting the function here would work well, but I thought it was worth demonstrating the use of the function call()
. Alternatively, you can use a function apply()
(discussing the difference between it and is call()
beyond the scope of this article, I can only say that it affects the way arguments are passed to the function). It’s
call()
great to use that we ourselves set the context in which the function is executed. This means that when we use a keyword this
inside our callback function, it refers to what we pass as the first argument tocall()
. In this example, when we referenced this inside our anonymous function, we referenced the responseXML resulting from the AJAX request. Finally, the second console.log expression will be executed first, because the callback function is not executed until the request is completed, and until this happens, the subsequent parts of the code continue to execute quietly.
Wrap it
I hope you now understand callbacks well enough to start using them in your own code. It’s still difficult for me to structure the code that is based on callback functions (in the end it becomes like spaghetti ... my mind is too used to ordinary structural programming), but they are a very powerful tool and one of the most interesting parts of the Javascript language.