0Pricing
Frontend Academy · Урок

Колбэки и ад колбэков

Разберитесь в асинхронном коде на основе колбэков, выявляйте проблему вложенности, известную как ад колбэков, и поймите, зачем были введены Promise для её решения.

«Колбэки и ад колбэков» — бесплатный урок Frontend Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Frontend Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Frontend Academy содержит 4 уроков всего.

Что такое функции обратного вызова

Функция обратного вызова — это функция, переданная в качестве аргумента другой функции, чтобы позднее вызвать её после завершения некоторой работы. Функции обратного вызова — исходный шаблон асинхронности в JavaScript; они появились раньше промисов.

// Synchronous callback:
[1, 2, 3].forEach(function(n) {
  console.log(n);
});

// Async callback:
setTimeout(function() {
  console.log('1 second later');
}, 1000);

Функции обратного вызова Node.js с ошибкой первым аргументом

Node.js стандартизировал соглашение: функции обратного вызова получают ошибку первым аргументом, а результат — вторым. Если значение ошибки равно null или undefined, операция завершилась успешно.

fs.readFile('data.json', 'utf8', function(err, data) {
  if (err) {
    console.error('Failed to read file:', err);
    return;
  }
  console.log('File contents:', data);
});

Асинхронные шаги с функциями обратного вызова

Когда одна асинхронная операция зависит от результата другой, функции обратного вызова вкладываются друг в друга. Уже три последовательных шага начинают выглядеть запутанно.

getUser(userId, function(err, user) {
  if (err) return handleError(err);
  getOrders(user.id, function(err, orders) {
    if (err) return handleError(err);
    getInvoice(orders[0].id, function(err, invoice) {
      if (err) return handleError(err);
      // Finally use invoice
      renderInvoice(invoice);
    });
  });
});

Ад функций обратного вызова — пирамида обречённости

Глубокая вложенность называется адом функций обратного вызова или пирамидой обречённости. Проблемы таковы: с каждым уровнем код смещается вправо, обработка ошибок повторяется, логику трудно проследить, а реорганизация кода становится мучительной.

Проблемы ада функций обратного вызова

1) Обработка ошибок требует явной проверки условия ошибки на каждом уровне. 2) Отладка затруднена: трассировки стека не показывают логическую цепочку. 3) Код трудно читать из-за горизонтальных уровней вложенности. 4) Ошибки легко случайно пропустить.

Способ уменьшить вложенность: именованные функции

Вынесите вложенные функции обратного вызова в именованные функции верхнего уровня. Это уменьшает вложенность и позволяет проверять каждый шаг изолированно, но не решает основную проблему координации.

function onInvoice(err, invoice) {
  if (err) return handleError(err);
  renderInvoice(invoice);
}
function onOrders(err, orders) {
  if (err) return handleError(err);
  getInvoice(orders[0].id, onInvoice);
}
function onUser(err, user) {
  if (err) return handleError(err);
  getOrders(user.id, onOrders);
}
getUser(userId, onUser);

Библиотеки управления потоком (историческая справка)

Библиотеки вроде async.js предоставляли вспомогательные средства (async.waterfall, async.parallel) для управления потоками на основе функций обратного вызова. До появления промисов в ES2015 это считалось передовым подходом.

Почему функции обратного вызова не исчезают

Функции обратного вызова по-прежнему встречаются повсюду: обработчики событий, методы массивов, setTimeout и API потоков используют их. Главное, что для асинхронного выполнения последовательности промисы и асинхронные функции с ожиданием удобнее. Для простых одноразовых событий функции обратного вызова по-прежнему подходят.

Преобразование функций обратного вызова в промисы

util.promisify() в Node.js оборачивает функции с ошибкой первым аргументом в версии, возвращающие промисы. Это связывает устаревшие API с современным кодом асинхронных функций и ожидания.

const { promisify } = require('util');
const readFile = promisify(require('fs').readFile);

async function readConfig() {
  const data = await readFile('config.json', 'utf8');
  return JSON.parse(data);
}

Когда функции обратного вызова по-прежнему подходят

Обработчики событий (addEventListener) ожидают функции обратного вызова, и это нормально: они срабатывают многократно. Обработчики данных потоков также ожидают функции обратного вызова. Промисы представляют одноразовые асинхронные результаты, а функции обратного вызова — подписки на множество событий.

Дальнейший путь: промисы и асинхронное ожидание

Промисы, появившиеся в ES2015, и асинхронное ожидание, появившееся в ES2017, решили проблему ада функций обратного вызова. Современный код JavaScript почти всегда использует асинхронные функции с ожиданием для последовательных асинхронных операций. Понимать функции обратного вызова по-прежнему важно для чтения старого кода и внутреннего устройства библиотек.

Быстрая проверка

Какую основную проблему описывает выражение «ад функций обратного вызова»?

Итоги: функции обратного вызова

Функции обратного вызова передаются другим функциям, чтобы вызвать их позднее. Это исходный шаблон асинхронности. Функции обратного вызова с ошибкой первым аргументом (ошибка, результат) — соглашение Node.js. Глубокая вложенность создаёт ад функций обратного вызова. Вынесение кода в именованные функции уменьшает вложенность. Для последовательных асинхронных потоков используйте промисы и асинхронные функции с ожиданием. Функции обратного вызова по-прежнему подходят для обработчиков событий и потоков.

Часто задаваемые вопросы

Урок «Колбэки и ад колбэков» бесплатный?

Да — полный текст урока «Колбэки и ад колбэков» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Frontend Academy, подпишись на CoddyKit PRO. Курс Frontend Academy содержит 4 уроков всего.

Чему я научусь в уроке «Колбэки и ад колбэков»?

Разберитесь в асинхронном коде на основе колбэков, выявляйте проблему вложенности, известную как ад колбэков, и поймите, зачем были введены Promise для её решения. Ты практикуешь Frontend Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Frontend Academy?

Предыдущий опыт не требуется. Frontend Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Колбэки и ад колбэков»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Frontend Academy?

Да. Каждый урок Frontend Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Цикл событий: стек вызовов, очередь и микрозадачи
  2. Колбэки и ад колбэков
  3. Promise: then, catch, finally и Promise.all
  4. async/await и обработка ошибок
← Назад к Frontend Academy