Колбэки и callback hell
Самый первый способ работать с асинхронностью в Node — и его главная проблема.
Колбэк (callback) — это функция, которую вы передаёте другой функции, чтобы её вызвали позже, когда операция завершится.
Идея колбэка
Когда операция занимает время (чтение файла, запрос), Node не возвращает результат сразу. Вместо этого вы говорите: «вот функция, вызови её, когда будет готово». Это и есть колбэк.
function loadData(callback) {
// имитируем задержку — данные приходят через 0 мс, но асинхронно
setTimeout(() => {
callback("данные готовы");
}, 0);
}
console.log("Запрашиваю данные...");
loadData((result) => {
console.log("Получено:", result);
});
console.log("Код продолжается, не дожидаясь");
Вывод:
Запрашиваю данные... Код продолжается, не дожидаясь Получено: данные готовы
Обратите внимание на порядок: программа не «застряла» на загрузке. Она пошла дальше, а колбэк сработал позже.
Соглашение error-first
В Node принят стандарт: первый аргумент колбэка — это ошибка (или null, если её нет), второй — результат. Это называют «error-first callback»:
const fs = require("fs");
fs.readFile("data.txt", "utf8", (err, data) => {
if (err) {
console.error("Ошибка чтения:", err.message);
return;
}
console.log("Содержимое:", data);
});
Сначала проверяем err, и только если ошибки нет — работаем с данными. Этот код использует fs, поэтому в браузере не запустится.
Callback hell — «ад колбэков»
Проблема начинается, когда операции зависят друг от друга: загрузи пользователя, потом его заказы, потом товары в заказах. Колбэки вкладываются друг в друга, и код «уезжает» вправо лесенкой:
getUser(1, (err, user) => {
getOrders(user.id, (err, orders) => {
getItems(orders[0].id, (err, items) => {
getDetails(items[0].id, (err, details) => {
console.log(details);
// и так далее — всё глубже и глубже
});
});
});
});
Такой код трудно читать, ещё труднее обрабатывать ошибки (их нужно проверять на каждом уровне) и почти невозможно сопровождать. Эту «лесенку вправо» и называют callback hell или «pyramid of doom».
Чем это плохо
- Глубокая вложенность — глаза теряются в уровнях.
- Обработку ошибок приходится повторять в каждом колбэке.
- Тяжело вставить шаг в середину или переиспользовать кусок.
Именно из-за этой боли в JavaScript появились промисы, а затем async/await — о них в следующих уроках. Они решают ровно эту проблему, делая асинхронный код плоским и читаемым.
Итог
- Колбэк — функция, которую вызывают по завершении асинхронной операции.
- В Node принято соглашение error-first: первый аргумент — ошибка.
- Вложенные зависимые колбэки превращаются в callback hell — «лесенку вправо».
- Промисы и async/await придуманы, чтобы избавиться от этой боли.