Понимание callback-функций (колбеков)
Функции — это объекты
Чтобы понимать callback-функции, нужно понимать обычные функции. Это может показаться банальностью, но функции в Javascript’е — немного странные штуки.
Преимущество концепции «функция-как-объект» заключается в том, что код можно передавать в другую функцию точно так же, как обычную переменную или объект (потому что в буквальном понимании код — всего лишь объект).
Передача функции как callback-функции
Передавать функцию в качестве аргумента просто.
Может показаться глупостью создавать такой перемудрённый код, когда можно вернуть значение нормальным способом, но существуют ситуации, в которых это непрактично и callback-функции необходимы.
Не загромождайте выход
Традиционно функции в ходе выполнения принимают на вход аргументы и возвращают значение, используя выражение return (в идеальном случае единственное выражение return в конце функции: одна точка входа и одна точка выхода). Это имеет смысл. Функции — это, в сущности, маршруты между вводом и выводом.
Javascript даёт возможность делать всё немного по-другому. Вместо того чтобы дожидаться, пока функция закончит выполняться и вернёт значение, мы можем использовать callback-функции, чтобы получить его асинхронно. Это полезно для случаев, когда требуется много времени для завершения, например, при AJAX-запросах, ведь мы не можем приостановить браузер. Мы можем продолжить заниматься другими делами в ожидании вызова колбека. Фактически, очень часто от нас требуется (или, точнее, нам настоятельно рекомендуется) делать всё асинхронно в Javascript’е.
Вот более детальный пример, в котором используется AJAX для загрузки XML-файла и используется функция call() для вызова callback-функции в контексте запрошенного объекта (это значит, что когда мы укажем ключевое слово this внутри callback-функции, оно будет ссылаться на запрошенный объект):
В этом примере мы создаём объект httpRequest и загружаем файл XML. Типичная парадигма возвращения значения в конце функции тут больше не работает. Наш запрос обрабатывается асинхронно, а это означает, что мы начинаем запрос и говорим ему вызвать нашу функцию, как только он закончится.
Мы используем здесь две анонимных функции. Важно помнить, что нам бы не составило труда использовать и именованные функции, но во имя лаконичности мы сделали их вложенными. Первая анонимная функция выполняется всякий раз при изменении статуса в нашем объекте httpRequest. Мы игнорируем это до тех пор, пока состояние не будет равно 4 (т.е. запрос выполнен) и статус будет равен 200 (т.е. запрос выполнен успешно). В реальном мире вам бы захотелось проверить, не провален ли запрос, но мы предполагаем, что файл существует и может быть загружен браузером. Эта анонимная функция связана с httpRequest.onreadystatechange, так что она выполняется не сразу, а вызывается каждый раз при изменении состояния в нашем запросе.
Что такое callback-функция в JavaScript?
Что такое коллбэк?
Простыми словами: коллбэк — это функция, которая должна быть выполнена после того, как другая функция завершила выполнение (отсюда и название: callback – функция обратного вызова).
Чуть сложнее: В JavaScript функции — это объекты. Поэтому функции могут принимать другие функции в качестве аргументов, а также функции могут возвращать функции в качестве результата. Функции, которые это умеют, называются функциями высшего порядка. А любая функция, которая передается как аргумент, называется callback-функцией. Чтобы лучше разобраться, давайте посмотрим на примерах, как это выглядит.
Зачем нам нужны коллбэки?
По одной простой причине: JavaScript — это событийно-ориентированный язык. Это значит, что вместо того, чтобы ждать ответа для дальнейшего выполнения программы, JavaScript продолжит выполнение, одновременно ожидая других событий. Давайте разберем простой пример:
Как вы и ожидаете, функция first выполнится первой, а функция second после нее, и в консоли будет выведен следующий результат:
Зачем я вам это показал? Чтобы вы понимали, нельзя просто вызывать функции в нужном порядке и надеяться, что они в любом случае выполнятся в том же порядке. Коллбэки же позволяют нам быть уверенными в том, что определенный код не начнет исполнение до того момента, пока другой код не завершит исполнение.
Создаем коллбэк
Хватит болтовни, теперь давайте создадим коллбэк.
Во-первых, откройте консоль разработчика в Google Chrome (Windows: Ctrl + Shift + J)(Mac: Cmd + Option + J) и введите в консоли следующую функцию:
Теперь давайте добавим в определение функции еще один параметр, это и будет наш коллбэк. Затем вызовем ее, определив функцию-callback в качестве аргумента:
Если вы введете этот код в консоли, вы получите два алерта один за другим, в первом будет сообщение о том, что выполнение домашнего задания началось (Starting my math homework.), а во втором — что вы закончили выполнять задание (Finished my homework).
Однако коллбэки не обязательно должны быть определены при вызове функции. Они могут быть определены и в другом месте кода, например, так:
Таким образом, результат выполнения этого кода такой же, как и в предыдущем примере, однако сам код немного другой. Как вы видите, мы передали функцию alertFinished как аргумент в функцию doHomework при ее вызове.
Пример из реальной жизни
На прошлой неделе я опубликовал статью «Создаем бота для Твиттера в 38 строк кода». Этот код работает благодаря API Твиттера. И когда мы делаем запрос к API, мы должны дождаться ответа до того, как начнем выполнять с этим ответом какие-то действия. Это прекрасный пример того, как в реальной жизни выглядит коллбэк. Вот как выглядит сам запрос:
T.get просто значит, что мы выполняем get запрос к API Твиттера. В запросе три параметра: ‘search/tweets’ – это адрес (роут) запроса, params – наши параметры поиска и в конце передается анонимная функция-callback.
Коллбэк здесь нужен, потому что нам нужно дождаться ответа от сервера до того, как приступим к дальнейшему выполнению кода. Мы не знаем, успешным будет наш запрос или нет, поэтому после отправки параметров поиска на search/tweets через get-запрос, мы просто ждем. Как только Твиттер ответит, выполнится наша callback-функция. Твиттер отправит нам в качестве ответа или объект err (error – ошибка), или объект response. В коллбэке мы можем через if() проверить, был ли запрос успешным или нет, и затем действовать соответственно.
Никогда не останавливайтесь: В программировании говорят, что нужно постоянно учиться даже для того, чтобы просто находиться на месте. Развивайтесь с нами — на Хекслете есть сотни курсов по разработке на разных языках и технологиях
С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.
Как работают колбэки в JavaScript
Если вы не читали первую часть — лучше начать с неё, потому что здесь мы будем разбирать и дополнять примеры кода оттуда.
В первой части мы на примере заказа пиццы разобрались с тем, зачем нужны колбэки. Сейчас посмотрим на то, как работают запросы к серверу, как получать данные через AJAX.
Практически любое приложение на JavaScript взаимодействует с сервером. Нам постоянно приходится получать данные с помощью методологии AJAX. Попробуем запросить информацию о произвольном аккаунте с GitHub и вывести её в консоль. Такая программа может выглядеть так:
Наша функция сработает сразу после получения ответа от сервера (произойдёт событие load ) и выведет ответ сервера в консоль.
Колбэки повсюду
Даже если вам не требуется работать с сервером, довольно высоки шансы столкнуться с функциями обратного вызова.
Всякий раз, когда вы пишете код, похожий на этот, вы применяете функции обратного вызова:
Как передать параметры в колбэк-функцию
Это очень важный вопрос, и в начале понимания сути колбэк-функций он может загнать в тупик.
Проблема в том, что мы привыкли передавать параметры явно: пишем имя функции, открываем круглые скобки и передаем значения. Этот подход применяется постоянно, но с колбэк-функциями дело обстоит иначе. Если мы укажем круглые скобки, то произойдёт вызов функции, а нам требуется передать ссылку на неё. Как поступить в этом случае?
Для этого нужно вспомнить про замыкания. Функции в JavaScript могут возвращать в качестве результата выполнения другие функции. А уже у них будет доступ к вышестоящей области видимости.
С таким подходом легко решить задачу передачи параметров в колбэк-функцию.
Код заказа пиццы, который мы написали в первой части, выглядел так:
Если представленный пример вызывает затруднение — обязательно перечитайте теорию замыканий.
Метод bind
Ад колбэков
Колбэк-функции просты в применении и открывают много возможностей, но с большой силой приходит большая ответственность. Легко потеряться во вложенности колбэк-функций и познать все прелести ада функций обратного вызова. Любым подходом нужно пользоваться с осторожностью. Как не попасть в ад обратных вызов, мы поговорим в одной из ближайших статей.






