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: Event Loop, промисы и async/await

    Асинхронность в JavaScript: Event Loop, промисы и async/await

    Аватар автора Асинхронность в JavaScript: Event Loop, промисы и async/await

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

    Иконка календаря04 июня 2026
    JavaScriptАсинхронностьEvent LoopПромисыasync/awaitmiddleИконка уровня middle
    Картинка поста Асинхронность в JavaScript: Event Loop, промисы и async/await

    Введение

    JavaScript — однопоточный язык, но при этом он отлично справляется с сетевыми запросами, таймерами и пользовательскими событиями без блокировки интерфейса. Секрет в асинхронной модели выполнения, построенной вокруг Event Loop. Понимание того, как движок обрабатывает очереди задач и микрозадач, отличает middle-разработчика от junior и помогает писать предсказуемый код.

    В статье разберём три кита асинхронности: цикл событий, промисы и синтаксис async/await. Покажем типичные грабли и способы их обойти.

    Как устроен Event Loop

    Движок V8 выполняет код в одном потоке через стек вызовов (Call Stack). Когда встречается асинхронная операция — setTimeout, fetch, чтение файла — она передаётся в Web API (или libuv в Node.js). По завершении колбэк попадает в одну из очередей: macrotask queue (таймеры, I/O, setImmediate) или microtask queue (промисы, queueMicrotask, MutationObserver).

    Event Loop работает по простому правилу: после очистки стека сначала выполняются ВСЕ микрозадачи, и только потом берётся одна макрозадача. После каждой макрозадачи очередь микрозадач снова осушается полностью.

    console.log('1: синхронный код');
    
    setTimeout(() => console.log('2: макрозадача'), 0);
    
    Promise.resolve().then(() => console.log('3: микрозадача'));
    
    console.log('4: синхронный код');
    
    // Порядок вывода: 1, 4, 3, 2
    // Микрозадача из промиса выполнится раньше setTimeout
    

    Если непрерывно создавать микрозадачи внутри микрозадач, макрозадачи (включая отрисовку и пользовательский ввод) будут заблокированы. Это редкий, но коварный способ повесить UI.

    Промисы: состояния и цепочки

    Промис — объект-обёртка над будущим результатом. У него три состояния: pending, fulfilled, rejected. Переход из pending в одно из конечных состояний необратим.

    function loadUser(id) {
      return new Promise((resolve, reject) => {
        // имитация сетевого запроса
        setTimeout(() => {
          if (id <= 0) {
            reject(new Error('Некорректный id'));
            return;
          }
          resolve({ id, name: 'Алиса' });
        }, 100);
      });
    }
    
    loadUser(1)
      .then(user => user.name.toUpperCase())
      .then(name => console.log('Имя:', name))
      .catch(err => console.error('Ошибка:', err.message))
      .finally(() => console.log('Запрос завершён'));
    

    Каждый then возвращает новый промис, поэтому цепочки можно собирать сколь угодно длинными. Ошибка из любого звена «проваливается» вниз до ближайшего catch — это сильно упрощает обработку сбоев по сравнению с колбэк-стилем.

    Параллельные запросы

    Когда задачи независимы, не нужно ждать их по очереди. Promise.all запускает их параллельно и возвращает массив результатов.

    const [user, orders, settings] = await Promise.all([
      fetch('/api/user').then(r => r.json()),
      fetch('/api/orders').then(r => r.json()),
      fetch('/api/settings').then(r => r.json())
    ]);
    

    Если важно дождаться всех результатов независимо от ошибок — берите Promise.allSettled. Нужен первый успешный — Promise.any. Нужен самый быстрый любого исхода — Promise.race.

    async/await: синхронный вид у асинхронного кода

    async/await — синтаксический сахар над промисами. Функция с ключевым словом async всегда возвращает промис, а await приостанавливает её до резолва без блокировки потока.

    async function fetchProfile(userId) {
      try {
        const user = await loadUser(userId);
        const posts = await fetch(`/api/posts?user=${user.id}`).then(r => r.json());
        return { user, posts };
      } catch (err) {
        // ошибки промисов ловятся обычным try/catch
        console.error('Не удалось загрузить профиль:', err);
        throw err;
      }
    }
    

    Код читается сверху вниз, как синхронный, но движок под капотом разворачивает его в цепочку then/catch. Стек ошибок в современных движках сохраняется корректно — отладка стала проще.

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

    1. Последовательный await вместо параллельного. Самая распространённая проблема производительности.

    // Плохо: 600 мс суммарно
    const a = await fetchA(); // 200 мс
    const b = await fetchB(); // 200 мс
    const c = await fetchC(); // 200 мс
    
    // Хорошо: 200 мс, запросы независимы
    const [a, b, c] = await Promise.all([fetchA(), fetchB(), fetchC()]);
    

    2. forEach с async-колбэком. Метод forEach игнорирует возвращаемые промисы — цикл не будет ждать завершения.

    // Не работает как ожидается
    items.forEach(async item => await save(item));
    console.log('готово'); // выведется ДО завершения сохранений
    
    // Правильно: for...of для последовательной обработки
    for (const item of items) {
      await save(item);
    }
    
    // Или Promise.all для параллельной
    await Promise.all(items.map(item => save(item)));
    

    3. Проглоченные ошибки. Промис без catch и без await выбрасывает unhandledRejection. В Node.js это приведёт к падению процесса в новых версиях.

    4. Лишний new Promise. Если функция уже возвращает промис — оборачивать её в new Promise не нужно, это антипаттерн promise constructor anti-pattern.

    Заключение

    Асинхронность в JavaScript держится на трёх уровнях абстракции: Event Loop как механизм планирования, промисы как объекты будущих значений и async/await как удобный синтаксис. Понимая порядок выполнения микро- и макрозадач, вы перестанете удивляться странному порядку логов и научитесь писать код, который не блокирует интерфейс.

    Главный практический совет: запускайте независимые операции параллельно через Promise.all, не теряйте ошибки, и помните, что await внутри обычных циклов работает, а внутри forEach — нет.

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

    Комментарии

    0

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

    Neovim - практика и настройка — часть карты развития Frontend, Backend, DevOps

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

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

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

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

    React и Redux Toolkit

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

    Zustand

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

    Next.js - с нуля

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

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

    Картинка поста Сколько стоит Cursor в 2026 и как оплатить из России
    Иконка аватараАнтон
    Иконка календаря17 октября 2026
    AI-инструментыAnthropicapijuniorИконка уровня junior

    Сколько стоит Cursor в 2026 и как оплатить из России

    С 12 октября 2026 года Cursor продаётся в составе The X Subscription — общей подписки на Grok, Cursor и X Premium. Тарифы такие: Free — бесплатно, Plus — $8 в месяц, Premium — $30, Super — $100, Ultra — $200. Модели Claude и GPT в Cursor доступны только с Premium и оплачиваются отдельно, по ценам API. Из России оплатить подписку российской картой нельзя, а модели Claude и GPT Cursor не отдаёт в регионах, которые блокируют их провайдеры. Рабочая схема для России — бесплатный Cursor как редактор и агент Claude Code внутри него с ключом AI для кода от PurpleSchool от 499 ₽ в месяц.

    Иконка чипа0
    Иконка глаза22
    Иконка комментариев0
    Картинка поста Claude без VPN в России: как пользоваться в 2026
    Иконка аватараАнтон
    Иконка календаря16 октября 2026
    AnthropicAI-инструментыClaude CodejuniorИконка уровня junior

    Claude без VPN в России: как пользоваться в 2026

    Сайт claude.ai и приложения Claude из России без VPN не работают: Anthropic не включает Россию в список поддерживаемых регионов и закрывает доступ и к чату, и к API. Без VPN Claude доступен одним способом — через API-посредника, который сам обращается к Anthropic из поддерживаемой страны, а вам выдаёт ключ с оплатой в рублях. Так работает AI для кода от PurpleSchool: ключ подключается к Claude Code, VS Code, Cursor и другим инструментам разработчика, а также к вашему собственному коду. Окно чата как на claude.ai такой способ не даёт — это доступ к модели, а не к сайту.

    Иконка чипа0
    Иконка глаза150
    Иконка комментариев0
    Картинка поста Как настроить CI/CD пайплайн с нуля: полное руководство
    Иконка аватараАнтон
    Иконка календаря14 октября 2026
    CI/CDDevOpsGitHub Actions+ 2middleИконка уровня middle

    Как настроить CI/CD пайплайн с нуля: полное руководство

    CI/CD пайплайн с нуля: как собрать рабочий конвейер сборки, тестов и деплоя на примере GitHub Actions и Docker без лишней теории.

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