Если вы не очень хорошо представляете себе — что такое «коллбэки», и как ими пользоваться в JavaScript, сейчас у вас есть шанс их понять и научиться с ними работать.
Перейдём сразу к делу. Коллбэк — это функция, которая должна быть выполнена после того, как другая функция завершит работу. Отсюда и название, которое, в английском написании, может быть представлено как «call back», хотя обычно это — «callback». Среди вариантов перевода этого слова — «обратный вызов». В русскоязычных публикациях, допускающих использование жаргона программистов, весьма распространена калька с оригинального названия: «коллбэк». Если же обойтись без жаргона, то о чём мы говорим, называется «функция обратного вызова».
Углубившись, для объяснения сущности функций обратного вызова, в особенности JavaScript, можно сказать, что функции в JS — это объекты. Поэтому функции могут принимать другие функции в качестве аргументов и возвращать их в качестве результатов. Функции, которые работают подобным образом, называют функциями высшего порядка. Коллбэками же обычно называют функции, передаваемые другим функциям в качестве аргументов.
Зачем нужны функции обратного вызова?
Коллбэки нужны по одной очень важной причине: JavaScript — это язык, в котором огромную роль играют события. Это означает, что вместо того, чтобы ожидать, скажем, результата выполнения некоей функции, остановив при этом все остальные операции, JavaScript-программа работает, наблюдая за событиями и реагируя на них.
Взглянем на простой пример:
function first(){
console.log(1);
}
function second(){
console.log(2);
}
first();
second();
Как можно ожидать, функция first()
выполняется первой, а функция second()
—
второй. Запуск этого кода приводит к тому, что в консоль будет выведено следующее:
// 1
// 2
Пока, надеемся, всё понятно, но что, если функция first()
содержит код, который нельзя выполнить немедленно? Например, там есть обращение к некоему API, причём, сначала нужно отправить запрос, а потом дождаться ответа? Для того, чтобы это сымитировать, воспользуемся функцией setTimeout()
, которая применяется в JavaScript для вызова других функций с заданной задержкой. Мы собираемся отложить вызов функции на 500 миллисекунд.
Вот что получилось теперь:
function first(){
// Имитируем задержку
setTimeout( function(){
console.log(1);
}, 500 );
}
function second(){
console.log(2);
}
first();
second();
Для наших целей особенности работы setTimeout()
сейчас неважны. Главное — обратите внимание на то, что вызов console.log(1)
будет выполнен с задержкой.
Вот что произойдёт при запуске этого кода:
// 2
// 1
Несмотря на то, что функция first()
была вызвана первой, сначала в лог попало то, что выводит функция second()
.
Это не значит, что JavaScript вызывает функции не в том порядке, в котором мы расположили их вызовы в коде. Смысл в том, что система переходит к исполнению функции second()
, не дожидаясь ответа от функции first()
.
В ситуациях, когда, вызвав некую функцию, нельзя быть уверенным в том, что программа продолжит работу только получив ответ от неё, использование функций обратного вызова — это подход, позволяющий гарантировать то, что некий фрагмент кода будет вызван только после того, как какой-то другой код завершит выполнение. Например, такое постоянно происходит в любой программе, которая так или иначе взаимодействует с внешним миром — скажем, с веб-сервисами.
Создаём функцию обратного вызова
Создадим собственную функцию обратного вызова.
Для начала — откройте консоль разработчика Chrome (Ctrl + Shift + J
в Windows, или Cmd + Option + J
в Mac) и введите следующее:
function doHomework(subject) {
alert(`Starting my ${subject} homework.`);
}
Тут мы объявили функцию doHomework()
. Эта функция принимает одну переменную — название предмета, по которому некто делает домашнюю работу. Вызовите функцию, введя в консоли следующее:
doHomework('math');
// Выводит сообщение: Starting my math homework.
Теперь добавим, в качестве второго аргумента функции doHomework()
, параметр callback
, который будем использовать для того, чтобы передать doHomework()
функцию обратного вызова. Теперь код будет выглядеть так:
function doHomework(subject, callback) {
alert(`Starting my ${subject} homework.`);
callback();
}
Вызовем обновлённую функцию следующими образом:
doHomework('math', function() {
alert('Finished my homework');
});
Сначала будет выведено сообщение с текстом Starting my math homework.
, потом — с текстом Finished my homework
.
Функции обратного вызова совсем необязательно создавать непосредственно при вызове функций, которым они передаются. Такую функцию можно объявить и где-нибудь в коде:
function doHomework(subject, callback) {
alert(`Starting my ${subject} homework.`);
callback();
}
function alertFinished(){
alert('Finished my homework');
}
doHomework('math', alertFinished);
После вызова функции doHomework()
всё будет выглядеть точно так же, как в предыдущем примере. Различия заключаются лишь в том, как мы работаем с функцией обратного вызова.
Как вы можете видеть, тут, в качестве аргумента при вызове функции doHomework()
, использовано имя функции alertFinished()
.
Функции обратного вызова в реальных проектах
Для программного взаимодействия с популярной социальной сетью Twitter используется специальное API. Выполняя обращения к этому API, мы вынуждены ждать ответа, и только после его получения можем выполнять с тем, что придёт от Twitter, какие-то действия. Вот материал, где рассмотрена работа с Twitter API в среде Node.js с использованием NPM-пакета twitter.
Рассмотрим фрагмент кода из этого материала. Полагаем, он является отличной демонстрацией практического применения функций обратного вызова.
T.get('search/tweets', params, function(err, data, response) {
if(!err){
// Именно здесь можно работать с тем, что вернёт нам Twitter
} else {
console.log(err);
}
})
T.get()
— это функция, которая выполняет get-запрос к Twitter API. У функции три аргумента. Первый — 'search/tweets'
, представляет собой маршрут запроса. Здесь мы собираемся выполнить поиск по твитам. Второй аргумент — params
— это параметры поиска. Третий аргумент — анонимная функция, которая и является функцией обратного вызова.
Функция обратного вызова здесь весьма важна, так как, прежде чем продолжать работу, нужно дождаться ответа от сервера. Неизвестно, будет ли обращение к API успешным, поэтому, после отправки параметров поиска по маршруту search/tweet
с помощью get-запроса, приходится ждать. Как только Twitter ответит на запрос, будет выполнена функция обратного вызова. Если что-то пошло не так, в ней мы получим объект ошибок (err
). Если запрос обработан нормально, в аргументе err
будет значение, эквивалентное false
, а значит, во-первых, будет исполнена ветвь if
условного оператора, а во-вторых — можно будет рассчитывать на то, что в объекте response
окажутся некие полезные данные, с которыми уже можно что-то делать.
Итоги
Надеемся, наш рассказ о функциях обратного вызова в JavaScript оказался полезен тем, кто не очень хорошо в них разбирался. На самом деле, то, о чём мы здесь говорили — это лишь вершина айсберга. Однако теперь, поняв основы, вы можете расширять и углублять свои знания в этой области.
Уважаемые читатели! Если вы из тех, кто, до чтения этого материала, плохо представлял себе, что такое функции обратного вызова в JS, скажите — стало понятнее? А если коллбэки для вас — обычное дело, просим поделиться опытом с новичками.
Автор: ru_vds