Как работают колбэки в JavaScript
- 11 октября 2019
Это вторая часть рассказа про то, как устроены колбэки в JavaScript.
В первой части мы на примере заказа пиццы разобрались с тем, зачем нужны колбэки. Сейчас посмотрим на то, как работают запросы к серверу, как получать данные через AJAX.
Практически любое приложение на JavaScript взаимодействует с сервером. Нам постоянно приходится получать данные с помощью методологии AJAX. Попробуем запросить информацию о произвольном аккаунте с GitHub и вывести её в консоль. Такая программа может выглядеть так:
const loadData = function(url, cb) {
const xhr = new XMLHttpRequest();
xhr.open(`GET`, url);
xhr.responseType = `json`;
// После получения данных выполним cb.
xhr.addEventListener(`load`, cb);
xhr.send();
}
loadData(`https://api.github.com/users/AntonovIgor`, function (evt) {
const response = evt.currentTarget.response;
console.log(response);
});
В этом коде мы делаем запрос к серверу с помощью объекта XMLHttpRequest
. Для удобства мы создали функцию loadData
, которая принимает два параметра: адрес сервера, который отдаст нам набор информации, и функцию обратного вызова. Вызов этой функции произойдёт сразу после получения данных от сервера. Если вас смутило наименование параметра cb
— не переживайте. Это общепринятый вариант обозначения функций обратного вызова (сокращение от callback). При виде cb
становится очевидно, что в этот параметр должна передаваться ссылка на функцию.
Мы не будем детально рассматривать содержимое функции loadData
, т. к. это не относится напрямую к теме статьи. Если коротко, то эта функция делает запрос к серверу и вызывает нашу функцию после получения ответа.
Обратите внимание, как мы вызываем функцию loadData
. В первый параметр мы передаём адрес сервера, а во второй — ссылку на функцию. В этот раз мы не описывали функцию отдельно, а сделали это прямо «в параметре» — создали анонимную функцию. Подобный подход часто применяется, когда функция нужна один раз. В таких ситуациях нет смысла писать отдельный код — достаточно создать анонимную функцию в нужном месте.
Наша функция сработает сразу после получения ответа от сервера (произойдёт событие load
) и выведет ответ сервера в консоль.
Колбэки повсюду
Даже если вам не требуется работать с сервером, довольно высоки шансы столкнуться с функциями обратного вызова.
Не верите? Хорошо, а как насчёт стандартных возможностей вроде перебирающих методов массивов: forEach
, every
, some
, reduce
, filter
или функции сортировки sort
, или методе addEventListener
(второй параметр принимает функцию, которая будет вызвана при наступлении события). Список можно продолжать до бесконечности.
Всякий раз, когда вы пишете код, похожий на этот, вы применяете функции обратного вызова:
[1, 2, 3, 4].forEach(function(it) {
console.log(it)
});
Аналогично с установкой обработчиков событий. Каждый раз, когда вы подписываетесь на событие с помощью addEventListener
, через её второй параметр вы определяете функцию обратного вызова, которая сработает при наступлении события. В мире JavaScript колбэки повсюду.
Как передать параметры в колбэк-функцию
Это очень важный вопрос, и в начале понимания сути колбэк-функций он может загнать в тупик.
Проблема в том, что мы привыкли передавать параметры явно: пишем имя функции, открываем круглые скобки и передаем значения. Этот подход применяется постоянно, но с колбэк-функциями дело обстоит иначе. Если мы укажем круглые скобки, то произойдёт вызов функции, а нам требуется передать ссылку на неё. Как поступить в этом случае?
Для этого нужно вспомнить про замыкания. Функции в JavaScript могут возвращать в качестве результата выполнения другие функции. А уже у них будет доступ к вышестоящей области видимости.
С таким подходом легко решить задачу передачи параметров в колбэк-функцию.
Пример
Код заказа пиццы, который мы написали в первой части, выглядел так:
const makePizza = function (title, cb) {
console.log(`Заказ на приготовление пиццы «${title}» получен. Начинаем готовить…`);
setTimeout(cb, 3000);
}
const readBook = function () {
console.log(`Читаю книгу «Колдун и кристалл»…`);
}
const eatPizza = function () {
console.log(`Ура! Пицца готова, пора подкрепиться.`);
}
makePizza(`Пеперонни`, eatPizza);
readBook();
Давайте модифицируем его и добавим для функции eatPizza
параметр drink
, через который будем передавать напиток:
const makePizza = function (title, cb) {
console.log(`Заказ на приготовление пиццы «${title}» получен. Начинаем готовить…`);
setTimeout(cb, 3000);
}
const readBook = function () {
console.log(`Читаю книгу «Колдун и кристалл»…`);
}
const eatPizza = function (drink) {
return function() {
console.log(`Ура! Пицца готова, пора подкрепиться и запить ${drink}.`);
}
}
makePizza(`Пепперони`, eatPizza(`Coca-Cola`));
readBook();
Первое, что мы сделали — внесли изменения в функцию eatPizza
. Теперь она принимает параметр drink
и возвращает новую функцию. В теле новой функции происходит вывод информации в консоль. Помимо текста, который у нас был, мы добавили вывод информации о напитке (drink
). Теперь самое интересное. Функция eatPizza
перестаёт быть функцией обратного вызова, вместо неё эту роль будет исполнять функция, которую возвращает eatPizza
.
Мы изменили тело функции, и теперь нам требуется обновить вызов makePizza
. Вторым параметром мы указываем не ссылку на eatPizza
, а вызов функции, передав информацию о напитке. Получается, на место второго параметра будет передана новая функция, полученная в результате выполнения eatPizza
.
Если представленный пример вызывает затруднение — обязательно перечитайте теорию замыканий.
Метод bind
Рассмотренный способ — не единственный. Любую задачу можно решить несколькими способами, и передача параметров колбэк-функции — не исключение. Мы можем воспользоваться методом bind
, который есть у любой функции.
Метод bind
всё упрощает. Результатом его вызова будет новая функция, которой мы можем передать произвольный набор параметров и задать контекст выполнения. Давайте исправим код заказа пиццы, используя метод bind
.
const makePizza = function (title, cb) {
console.log(`Заказ на приготовление пиццы «${title}» получен. Начинаем готовить…`);
setTimeout(cb, 3000);
}
const readBook = function () {
console.log(`Читаю книгу «Колдун и кристалл»…`);
}
const eatPizza = function (drink) {
console.log(`Ура! Пицца готова, пора подкрепиться и запить ${drink}.`);
}
makePizza(`Пепперони`, eatPizza.bind(null, `Coca-Cola`));
readBook();
Мы вернули функцию eatPizza
к первоначальному состоянию, избавившись от возврата новой функции. В остальном всё без изменений. При передаче функции обратного вызова мы воспользовались методом bind
. Первый параметр (null
) позволяет задать произвольный контекст для функции, но так как эта возможность нас не интересует, мы передаем null
. Во втором параметре мы передаем значение для параметра drink
. Мы можем передать любое количество параметров для функции обратного вызова, перечисляя их через запятую.
Ад колбэков
Колбэк-функции просты в применении и открывают много возможностей, но с большой силой приходит большая ответственность. Легко потеряться во вложенности колбэк-функций и познать все прелести ада функций обратного вызова. Любым подходом нужно пользоваться с осторожностью. Как не попасть в ад обратных вызов, мы поговорим в одной из ближайших статей.
«Доктайп» — журнал о фронтенде. Читайте, слушайте и учитесь с нами.
Читать дальше

AOT против JIT-компилятора: что лучше для разработки на Angular?
Angular — один из самых популярных фреймворков для фронтенда — предлагает два подхода к компиляции: предварительная компиляция и динамическая компиляция во время выполнения. Оба метода играют важную роль в оптимизации приложений на Angular и повышении их производительности. В этом материале мы рассмотрим различия между ними, их преимущества и разберёмся, когда стоит использовать каждый из подходов.
- 25 мая 2025

Динамические формы в Angular 19: пошаговое руководство
Формы — неотъемлемая часть большинства веб-приложений: будь то регистрация, ввод данных или опросы. Модуль реактивных форм в Angular отлично подходит для создания статичных форм, но во многих случаях требуется, чтобы форма могла динамически адаптироваться в зависимости от действий пользователя или внешних данных.
В этой статье мы рассмотрим, как создавать динамические формы с использованием автономных компонентов в Angular 19, применяя модульный подход, который избавляет от необходимости использовать традиционные модули Angular. В сопроводительном репозитории на GitHub для оформления форм используется Tailwind CSS, однако в статье внимание сосредоточено исключительно на логике динамических форм. Tailwind и связанные с ним настройки намеренно не включены в примеры, чтобы сохранить акцент на основной теме.
- 25 мая 2025

Как обнаружить изменения в Angular: пошаговая инструкция
Как разработчики на Angular, мы нередко задумываемся, как фреймворк отслеживает изменения в данных и затем отображает их во вьюхе. Этот процесс называется стратегией обнаружения изменений в Angular. В этом материале мы разберёмся, как это работает, и научимся выбирать подходящую стратегию для разных сценариев.
- 24 мая 2025

Компоненты в Angular 18: пошаговое руководство
Angular развивается стремительно, и с выходом версии 18 появились новые возможности, которые разработчики могут использовать в своей работе. Одним из ключевых изменений в Angular 18 стало удаление традиционного файла app.module.ts — ему на смену пришли standalone-компоненты. Если вы только начинаете работать с Angular или переходите с более ранней версии, это пошаговое руководство поможет вам разобраться в базовых принципах компонентов в Angular 18. Независимо от вашего уровня — новичок вы или опытный разработчик — этот туториал покажет, как создавать, управлять и эффективно использовать компоненты в Angular.
- 19 мая 2025

Полное руководство по Angular @if
Одно из самых заметных нововведений в Angular — это встроенный синтаксис для управляющих конструкций, который появился в версии 17. Он решает одну из самых частых задач, с которой сталкивается каждый разработчик: показывать или скрывать элементы на странице в зависимости от условия. Раньше для этого использовали привычную структурную директиву *ngIf
. Теперь у нас есть более современная альтернатива — синтаксис @if
, часть нового подхода к управлению шаблоном.
В этом гайде мы сравним оба варианта, разберёмся, чем @if
лучше, и покажем, как можно перейти на него автоматически. Также поговорим об одной распространённой ошибке — о том, как не стоит использовать @if
вместе с пайпом async
.
- 18 мая 2025

Модули Angular для организации кода и ленивой загрузки
Модули — один из ключевых инструментов Angular для построения масштабируемых и поддерживаемых приложений. В этой статье мы подробно рассмотрим:
- что такое модули в Angular;
- зачем они нужны;
- как их использовать для структурирования кода;
- как реализовать «ленивую» загрузку модулей;
- и чем отличаются Feature, Core и Shared модули.
Если вы только начинаете изучать Angular или хотите углубить свои знания, эта статья поможет вам лучше понять, как правильно организовать архитектуру Angular-приложения.
- 12 мая 2025

Навигация в Angular: RouterLink, Router.navigate и Router.navigateByUrl
Директива RouterLink
позволяет настраивать переходы между маршрутами прямо в шаблоне Angular. А методы Router.navigate
и Router.navigateByUrl
, доступные в классе Router
, дают возможность управлять навигацией программно — прямо из кода компонентов.
Разберёмся, как работают RouterLink
, Router.navigate
и Router.navigateByUrl
.
- 11 мая 2025

Полное руководство по Lazy Loading в Angular
Если вы создаёте большое Angular-приложение, вам наверняка важно, чтобы оно загружалось быстро. Представьте, что вы устраиваете вечеринку и хотите подавать закуски не сразу, а по мере прихода гостей, чтобы не перегрузить кухню. «Ленивая» загрузка в Angular работает примерно так же: вместо того чтобы загружать всё приложение целиком сразу, вы подгружаете только те части, которые нужны — и только когда они нужны.
В этом пошаговом руководстве мы разберём, как реализовать lazy loading в Angular.
- 10 мая 2025

Все (ну или почти все) способы автоматически перезагрузить страницу раз в N секунд
Иногда страницу нужно просто перезагрузить. Полностью. Не компонент, не блок, а именно целиком. Без обсуждений, без лишней логики. Например, чтобы:
- экран с результатами обновлялся каждые 10 секунд;
- интерфейс на стенде показывал последние данные без кнопок;
- страницы в интранете не устаревали, пока никто не смотрит.
Это можно сделать в любой связке: HTML, JS, Python, PHP, Go, Node.js — не важно. Ну и если говорить совсем прямо, то совсем разных способов всего три, а остальное просто вариации.
- 5 мая 2025

Поиск с конца массива в JavaScript: findLast и findLastIndex в JavaScript
С недавних пор в JavaScript появились два полезных метода для массивов: findLast
и findLastIndex
. Они делают то же, что и привычные find
и findIndex
, только проходят массив с конца.
- 5 мая 2025