PurpleSchool — курсы программирования онлайн
  • Пути
    • Frontend React разработчик
    • Frontend Vue разработчик
    • Backend разработчик Node.js
    • Fullstack разработчик React / Node.js
    • Mobile разработчик React Native
    • Backend разработчик Golang
    • Devops инженер
    • Backend разработчик Python
  • AI для кодаНовое
  • О нас
    • Отзывы
    • Реферальная программа
    • О компании
    • Контакты
  • Иконка открытия меню
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
PurpleSchool — платформа бесплатных roadmap и курсов для разработчиков
ютуб иконка
Telegram иконка
VK иконка
VK иконка
Курсы
ГлавнаяКаталог курсовFrontendBackendFullstack
Практика
КарьераПроектыPurpleПлюс
Материалы
БлогБаза знаний
Документы
Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
Реквизиты
ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развитияВопросы для собеседований
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • PurpleSchool — курсы программирования онлайн
    • AI для кодаНовое
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
    Главная
    Сообщество
    Асинхронный JavaScript: callbacks, Promise, async/await по порядку

    Асинхронный JavaScript: callbacks, Promise, async/await по порядку

    Аватар автора Асинхронный JavaScript: callbacks, Promise, async/await по порядку

    Антон Ларичев

    Иконка календаря19 мая 2026
    JavaScriptАсинхронностьPromiseasync/awaitjuniorИконка уровня junior
    Картинка поста Асинхронный JavaScript: callbacks, Promise, async/await по порядку

    Введение

    Асинхронный JavaScript — это фундамент современной веб-разработки. Браузер и Node.js работают в одном потоке, поэтому без асинхронных механизмов любая долгая операция (запрос к серверу, чтение файла, таймер) блокировала бы интерфейс. В этой статье разберём три подхода к работе с асинхронностью в порядке их появления: callbacks, Promise и async/await. Поймём, зачем каждый из них появился и какие проблемы решал.

    Callbacks: с чего всё началось

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

    // Имитация запроса к серверу с коллбэком
    function loadUser(id, callback) {
      setTimeout(() => {
        // Возвращаем пользователя через 1 секунду
        callback(null, { id, name: 'Анна' });
      }, 1000);
    }
    
    loadUser(1, (error, user) => {
      if (error) {
        console.error('Ошибка загрузки:', error);
        return;
      }
      console.log('Пользователь:', user);
    });
    

    Первым параметром коллбэка по соглашению передают ошибку (error-first callback), вторым — результат. Это позволяет единообразно обрабатывать сбои.

    Callback hell

    Когда нужно выполнить несколько асинхронных операций последовательно, код быстро превращается в «лестницу» из вложенных функций:

    loadUser(1, (err, user) => {
      if (err) return console.error(err);
      loadPosts(user.id, (err, posts) => {
        if (err) return console.error(err);
        loadComments(posts[0].id, (err, comments) => {
          if (err) return console.error(err);
          // Глубина вложенности растёт с каждой операцией
          console.log(comments);
        });
      });
    });
    

    Такой код сложно читать, отлаживать и поддерживать. Именно эта проблема породила следующий подход.

    Promise: контракт на будущее значение

    Promise — это объект, представляющий результат асинхронной операции, которая ещё не завершилась. Появился в стандарте ES2015. У промиса три состояния: pending (ожидание), fulfilled (выполнен) и rejected (отклонён).

    function loadUser(id) {
      // Возвращаем промис вместо приёма коллбэка
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          if (id <= 0) {
            reject(new Error('Неверный идентификатор'));
            return;
          }
          resolve({ id, name: 'Анна' });
        }, 1000);
      });
    }
    
    loadUser(1)
      .then((user) => console.log('Пользователь:', user))
      .catch((error) => console.error('Ошибка:', error));
    

    Главное преимущество — цепочки .then(). Каждый then возвращает новый промис, поэтому асинхронные операции выстраиваются в плоскую последовательность:

    loadUser(1)
      .then((user) => loadPosts(user.id))
      .then((posts) => loadComments(posts[0].id))
      .then((comments) => console.log(comments))
      .catch((error) => console.error('Где-то в цепочке ошибка:', error));
    

    Один .catch() в конце ловит ошибки со всех шагов — это намного удобнее проверки if (err) в каждом коллбэке.

    Параллельный запуск

    Promise.all позволяет дождаться выполнения нескольких независимых операций параллельно:

    // Запускаем три запроса одновременно, ждём все
    Promise.all([loadUser(1), loadUser(2), loadUser(3)])
      .then((users) => console.log('Все загружены:', users))
      .catch((error) => console.error(error));
    

    Есть также Promise.allSettled (ждать все, даже отклонённые), Promise.race (первый завершившийся) и Promise.any (первый успешный).

    async/await: синтаксический сахар над Promise

    Конструкция async/await появилась в ES2017 и сделала асинхронный код визуально похожим на синхронный. Под капотом это всё те же промисы.

    // Функция, помеченная async, всегда возвращает промис
    async function showUserData(id) {
      try {
        const user = await loadUser(id);
        const posts = await loadPosts(user.id);
        const comments = await loadComments(posts[0].id);
        console.log(comments);
      } catch (error) {
        // Ловим ошибки любого из await обычным try/catch
        console.error('Ошибка:', error);
      }
    }
    
    showUserData(1);
    

    Ключевое слово await приостанавливает выполнение функции до завершения промиса и возвращает его результат. Использовать await можно только внутри async-функций или на верхнем уровне модуля.

    Параллельность в async/await

    Частая ошибка — забывать о параллельности. Этот код выполняется последовательно и медленно:

    // Плохо: каждый запрос ждёт предыдущий
    const user1 = await loadUser(1);
    const user2 = await loadUser(2);
    

    Правильно — запустить промисы одновременно и дождаться обоих:

    // Хорошо: запросы идут параллельно
    const [user1, user2] = await Promise.all([loadUser(1), loadUser(2)]);
    

    Частые ошибки

    Забытый return в then. Если внутри .then() не вернуть промис, следующий шаг цепочки не дождётся его завершения и получит undefined.

    Смешивание подходов. Не оборачивайте промисы в коллбэки и не создавайте new Promise вокруг уже существующего промиса — это антипаттерн, известный как «explicit promise construction».

    Отсутствие обработки ошибок. Промис без .catch() или await без try/catch приведёт к unhandled rejection. В современных средах это уже считается ошибкой.

    await в цикле forEach. Метод forEach не понимает асинхронности — используйте обычный for...of, если нужна последовательность, или Promise.all с map, если порядок не важен.

    Игнорирование возвращаемого промиса. Вызов async-функции без await запускает её, но не ждёт результата. Иногда это нужно, но чаще — источник скрытых багов.

    Заключение

    Коллбэки, промисы и async/await — это не конкурирующие технологии, а эволюция одной идеи. Промисы решили проблему callback hell, а async/await сделал работу с промисами максимально близкой к синхронному коду. На практике в современных проектах вы будете писать преимущественно async/await, но понимать промисы под капотом необходимо: без этого сложно отлаживать ошибки и грамотно использовать Promise.all, Promise.race и другие комбинаторы. Коллбэки же по-прежнему встречаются в старых API и низкоуровневых модулях Node.js — знать их полезно, чтобы при необходимости легко обернуть в промис через util.promisify или вручную.

    Иконка глаза592

    Комментарии

    0

    Постройте личный план изучения HTML и CSS - полный курс по вёрстке с нуля до уровня Middle — бесплатно!

    HTML и CSS - полный курс по вёрстке с нуля — часть карты развития Frontend, Mobile

    • step100+ шагов развития
    • lessons30 бесплатных лекций
    • lessons300 бонусных рублей на счет

    Бесплатные лекции

    Лучшие курсы по теме

    изображение курса

    CSS Flexbox

    Антон Ларичев
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.9
    бесплатно
    Подробнее
    изображение курса

    Основы JavaScript

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Продвинутый JavaScript

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее

    Похожие статьи

    Иконка аватараАнтон
    Иконка календаря01 октября 2026
    AI-инструментыDevOpsразработка+ 2middleИконка уровня middle

    Gemini CLI в России: как установить и настроить

    Установить Gemini CLI из России можно без VPN и без ухищрений — это обычный npm-пакет, он скачивается и запускается на любой машине. Упирается всё на следующем шаге: 18 июня 2026 года Google перестал обслуживать запросы Gemini CLI для бесплатных аккаунтов и подписчиков Google AI Pro и Ultra, а единственный оставшийся путь — вход по API-ключу — требует биллинга в регионе, где Россия не числится. Ниже разбираем, как агент ставится, как устроены три способа входа, почему ни один из них не доводи...

    Иконка чипа0
    Иконка глаза66
    Иконка комментариев0
    Картинка поста Git флоу для команды разработчиков: гайд по веткам и релизам
    Иконка аватараАнтон
    Иконка календаря30 сентября 2026
    gitgit-flowкомандная работа+ 2middleИконка уровня middle

    Git флоу для команды разработчиков: гайд по веткам и релизам

    Git флоу для команды разработчиков: разбираем Git Flow, GitHub Flow и trunk-based development — как выбрать модель веток и избежать конфликтов при совместной работе.

    Иконка чипа0
    Иконка глаза75
    Иконка комментариев0
    Иконка аватараАнтон
    Иконка календаря29 сентября 2026
    AI-инструментыClaude CodeDevOps+ 2middleИконка уровня middle

    Бесплатный аналог Cursor и Claude Code в России

    Короткий ответ: бесплатных аналогов много, и почти все они ставятся в России без ухищрений — Cline, Kilo Code, OpenCode, Qwen Code, Codex CLI распространяются с открытым исходным кодом, и за сам инструмент никто денег не берёт. Проблема в другом: своей модели у них нет. Платите вы не за агента, а за модель, к которой он ходит, и блокируется из России именно доступ к модели. Поэтому вопрос «чем заменить Cursor и Claude Code» почти всегда оказывается другим вопросом — где взять доступ к сильной модели без зарубежной карты. Ниже разберём, что из бесплатного реально работает, где оно упирается в потолок, и во сколько обходится не искать замену вообще. {{IMAGE_1}} alt: Схема: бесплатные клиенты-агенты Cline, Kilo Code, OpenCode, Qwen Code и платный доступ к модели за ними prompt: Flat vector diagram on dark background, two clearly separated zones. Left zone labeled "free" contains five small rounded cards representing code editor agents with terminal and sidebar icons. Right zone labeled "paid" contains a single large cloud icon representing an AI model, with a lock badge. Arrows go from all five cards to the single cloud. Purple and violet accent colors, thin white lines, no text labels, clean minimal tech illustration, 16:9

    Иконка чипа0
    Иконка глаза281
    Иконка комментариев0
    Иконка чипа0