Колбэки #
Колбэк (callback) — функция, которую передают другой функции, чтобы та вызвала её позже: после события, таймера или ответа сети. До появления промисов колбэки были основным способом описывать асинхронность в JavaScript.
Синхронный и асинхронный колбэк #
Синхронный колбэк выполняется сразу внутри вызывающего кода:
const playlist = ['Neon Drive', 'Static Bloom', 'Low Tide']
playlist.forEach((track, index) => {
console.log(`${index + 1}. ${track}`)
})
Асинхронный колбэк откладывается — типичный пример setTimeout:
function showToast(message, onHidden) {
console.log(message)
setTimeout(() => {
onHidden?.()
}, 2000)
}
showToast('Трек добавлен в плейлист', () => {
console.log('Тост скрыт')
})
Вызывающий код не ждёт завершения колбэка: выполнение идёт дальше, а результат приходит позже.
Имитация сетевого запроса #
В учебных примерах API часто моделируют таймером:
function fetchTrackMeta(trackId, callback) {
const fakeDb = {
'tr-01': { title: 'Neon Drive', bpm: 118 },
'tr-02': { title: 'Static Bloom', bpm: 92 },
}
setTimeout(() => {
const meta = fakeDb[trackId]
if (!meta) {
callback(new Error(`Трек ${trackId} не найден`), null)
return
}
callback(null, meta)
}, 300)
}
fetchTrackMeta('tr-01', (err, meta) => {
if (err) {
console.error(err.message)
return
}
console.log(`${meta.title}, ${meta.bpm} BPM`)
})
Здесь используется распространённое соглашение Node.js: первый аргумент — ошибка (null при успехе), второй — данные.
Колбэк как завершение операции #
Браузерные API исторически строились на колбэках: addEventListener, readAsText у FileReader, старые варианты XHR. Паттерн один: «запусти операцию → вызови функцию, когда готово».
function readPlaylistFile(file, onResult) {
const reader = new FileReader()
reader.onload = () => onResult(null, reader.result)
reader.onerror = () => onResult(reader.error, null)
reader.readAsText(file)
}
Разделение onload / onerror — вариант того же принципа: разные колбэки для успеха и сбоя.
Пирамида колбэков (callback hell) #
Когда шаги зависят друг от друга, колбэки вкладывают друг в друга:
fetchTrackMeta('tr-01', (err, meta) => {
if (err) return handle(err)
fetchTrackMeta('tr-02', (err2, meta2) => {
if (err2) return handle(err2)
mergeIntoPlaylist([meta, meta2], (err3) => {
if (err3) return handle(err3)
renderPlaylist(() => {
// ещё один уровень вложенности...
})
})
})
})
Проблемы такого стиля:
| Проблема | Последствие |
|---|---|
| Глубокая вложенность | Трудно читать и сопровождать |
Дублирование if (err) | Шум и риск пропустить проверку |
| Параллельные запросы | Сложно координировать без счётчиков |
| Обработка одной ошибки | Нет единой точки .catch |
Промисы и async/await решают те же задачи с плоской структурой и явной передачей ошибок по цепочке.
Инверсия управления #
Колбэк передаёт когда и с какими аргументами вызвать ваш код — управление переворачивается: не вы вызываете библиотеку до конца, а библиотека вызывает вас. Это мощно, но требует дисциплины: всегда обрабатывайте ошибку, не вызывайте колбэк дважды, документируйте порядок аргументов.
Итог #
- Колбэк — функция, переданная для вызова позже; основа событий и старых async API.
- Соглашение
(err, data)упорядочивает обработку сбоев в Node-стиле. - Вложенные зависимые шаги приводят к «пирамиде» и дублированию проверок.
- Промисы и
async/await— эволюция той же идеи с более читаемым синтаксисом.