Интервью-вопрос
Callback является функцией или объектом
Callback это функция, которую передают для последующего вызова. Технически функция в JavaScript является объектом, но на практике важно говорить о роли callback, времени вызова и обработке ошибок.
- Добавлен
- Редакция
Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.
Мини-квиз
Проверка перед разбором
Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.
Вопрос 1 из 50 правильно
Разбор
Разобраться, а не зазубрить
Дальше разбираем суть, типичные уточнения и места, где легко сказать лишнее или перепутать термины.
Базовая идея
Callback это не отдельный тип данных. Это роль функции в коде. Функция становится callback, когда вы передаете ее в другой код и ожидаете, что этот другой код вызовет ее в нужный момент.
button.addEventListener("click", () => {
console.log("Клик обработан");
});Функция внутри addEventListener это callback. Она не выполняется сразу при чтении строки. Браузер вызовет ее позже, когда пользователь нажмет кнопку.
Почему тогда говорят про объект
В JavaScript функции являются объектами первого класса. Это значит, что функцию можно положить в переменную, передать в аргумент, вернуть из другой функции и добавить ей свойства.
function onDone(result) {
console.log(result);
}
console.log(typeof onDone); // "function"
console.log(onDone.name); // "onDone"typeof для функции вернет "function". При этом функция имеет свойства и методы, например name, length, call, apply, bind. Поэтому точная формулировка такая: callback это функция по назначению, а функция в JavaScript технически является объектом.
Как построить ответ
Начните с роли: callback это функция для последующего вызова.Добавьте, что функции в JavaScript являются объектами первого класса.Говорите про момент вызова, ошибки, повторный вызов и устаревшие данные.Объясните, что Promise и async/await часто удобнее для цепочек и обработки ошибок.Практический риск для фронтенда
На практике вопрос не только про термин. Важно понимать, кто управляет вызовом callback. Если вы передали функцию в браузерный API, библиотеку или свой helper, вы уже не вызываете ее напрямую. Это влияет на ошибки, состояние UI и очистку ресурсов.
Плохой пример:
try {
setTimeout(() => {
throw new Error("Не удалось обновить данные");
}, 1000);
} catch (error) {
console.log("Поймали ошибку", error);
}Этот catch не поймает ошибку из таймера, потому что callback выполнится позже, когда внешний стек уже завершился. Безопаснее обработать ошибку внутри callback или использовать Promise API, где ошибка попадет в catch или try/catch вокруг await.
Более безопасная версия для callback выглядит так:
setTimeout(() => {
try {
throw new Error("Не удалось обновить данные");
} catch (error) {
console.error(error);
showErrorMessage("Попробуйте еще раз");
}
}, 1000);В реальном UI вместо простого console.error обычно нужно закрыть loader, показать понятную ошибку и не оставлять экран в подвешенном состоянии.
Callback в своем API
Если вы сами пишете функцию, которая принимает callback, договоритесь о контракте. Минимум нужно понимать, какие аргументы получит callback, сколько раз он может быть вызван и как передается ошибка.
function loadUser(userId, callback) {
fetch(`/api/users/${userId}`)
.then((response) => {
if (!response.ok) {
throw new Error("User loading failed");
}
return response.json();
})
.then((user) => callback(null, user))
.catch((error) => callback(error, null));
}Здесь используется старый стиль error-first callback: первым аргументом идет ошибка, вторым данные. Такой контракт надо проговаривать явно. Иначе вызывающий код может принять ошибку за данные или наоборот.
- 1Проверить, что аргумент является функцией
- 2Описать порядок аргументов
- 3Вызвать callback один раз для одного результата
- 4Передать ошибку явно или обработать ее внутри
- 5Не обновлять UI после отмененной операции
- 1Принять любой аргумент и сразу вызвать его
- 2Смешать данные и ошибку без понятного правила
- 3Вызвать callback несколько раз случайно
- 4Проигнорировать Promise из async callback
- 5Оставить обработчики без cleanup
Callback, Promise и async/await
Callback и Promise часто встречаются рядом, но это разные модели. Callback это функция, которую кто-то должен вызвать. Promise это объект, который представляет будущий результат операции и имеет состояние: ожидание, успех или ошибка.
Если код становится вложенным, Promise и async/await обычно читаются проще:
async function showUser(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error("User loading failed");
}
const user = await response.json();
renderUser(user);
} catch (error) {
showErrorMessage("Не удалось загрузить пользователя");
}
}Это не значит, что callback устарел везде. События DOM, таймеры, некоторые браузерные API и legacy-библиотеки все еще используют callback. Сильный ответ показывает, что вы понимаете оба подхода и выбираете их по контракту, а не по моде.
Нюанс с async callback
async функция может быть callback, потому что это все еще функция. Но результат такой функции всегда Promise. Проблема начинается, если вызывающий API этот Promise не ожидает.
element.addEventListener("click", async () => {
await saveAnalyticsEvent();
showSuccessMessage();
});Такой код может быть нормальным, если ошибка внутри обработана или некритична. Но браузерный addEventListener не будет ждать Promise как часть своего контракта. Если saveAnalyticsEvent упадет и вы не поймаете ошибку, пользователь может не увидеть обратную связь, а в консоли появится необработанная ошибка.
Опасный вариант в UI: добавить callback как обработчик и не снять его при cleanup.
function subscribeButton(button) {
button.addEventListener("click", () => {
sendAnalytics("button_click");
});
}Если такую функцию вызвать несколько раз, один клик отправит несколько событий. Если элемент или экран уже неактуален, старый callback все равно может сработать. Безопаснее хранить ссылку на обработчик и снимать ее, когда подписка больше не нужна.
function subscribeButton(button) {
const onClick = () => {
sendAnalytics("button_click");
};
button.addEventListener("click", onClick);
return () => {
button.removeEventListener("click", onClick);
};
}Практический вывод
На интервью лучше не спорить, выбрать одно слово или другое. Дайте формулировку, которая закрывает обе стороны вопроса:
Callback это функция, которую передают в другой код для последующего вызова. В JavaScript функции являются объектами первого класса, поэтому технически callback тоже имеет объектную природу. Но главное в callback не это, а контракт вызова: когда его вызовут, с какими аргументами, сколько раз и как будут обработаны ошибки.
Такой ответ звучит точнее, чем просто "функция" или просто "объект". Он показывает, что вы понимаете и язык, и практические последствия для frontend-кода.
Частые ошибки
Где обычно ошибаются
Проверьте формулировки, которые звучат уверенно, но на интервью быстро выдают пробелы.
- 1
Называть callback обычным объектом
Так вы теряете главный смысл. Callback важен не потому, что у функции есть свойства, а потому что ее передают для вызова в нужный момент. Скажите: callback это функция, а в JavaScript функции технически являются объектами. - 2
Путать callback и Promise
Promise сам хранит состояние операции, а callback просто вызывается чужим кодом. Если смешать эти понятия, легко ошибиться в обработке ошибок и ожидании результата. На практике это приводит к зависшим загрузчикам и необработанным ошибкам. - 3
Думать, что async callback всегда ожидается
asynccallback возвращает Promise, но не каждый API его использует. Например, обработчик события в браузере не будет ждать вашawaitкак часть своего контракта. Если ошибка внутри не обработана, она может стать unhandled rejection. - 4
Не говорить про момент вызова
Callback может быть вызван синхронно, после таймера, после ответа сети или при событии пользователя. Без этого уточнения ответ звучит как определение из учебника. Для фронтенда момент вызова влияет на состояние UI, cleanup и гонки данных. - 5
Не проверять контракт callback API
Если функция принимает callback, важно понимать, какие аргументы она передает и сколько раз вызывает callback. Иначе можно дважды показать toast, дважды отправить аналитику или обновить состояние после размонтирования компонента. - 6
Забывать cleanup для callback из UI API
Обработчики событий, таймеры и подписки живут дольше одной функции. Если не снять их при уходе со страницы или размонтировании компонента, старый callback может обновить неактуальный UI, отправить лишний запрос или удерживать данные в памяти.
Follow-up
Что могут спросить дальше
Короткие ответы на вопросы, которыми интервьюер проверяет понимание callback, функций и асинхронного кода.
Живые ответы
Видео с похожим вопросом
Если найдем публичные интервью с таким вопросом, добавим их сюда. Их удобно смотреть после теории, чтобы свериться с живыми ответами.
Пока видео нет. Когда появятся подходящие публичные интервью, добавим их в этот блок, чтобы можно было сравнить разбор с тем, как отвечают реальные кандидаты.
Что будет при использовании forEach для объекта 😎
У обычного объекта нет метода forEach, поэтому прямой вызов приведет к TypeError. Разбираем, как безопасно перебирать свойства объекта и где легко ошибиться во frontend-коде.
Как проверить, является ли значение массивом в JavaScript 😎
Надежный способ проверки массива в JavaScript это Array.isArray(value). Разбираем, почему typeof и instanceof могут подвести и что сказать на интервью.