← Назад к вопросам

Что внутри async/await?

2.0 Middle🔥 251 комментариев
#JavaScript Core#Браузер и сетевые технологии

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Внутреннее устройство async/await

Async/await — это синтаксический сахар над Promise, который делает асинхронный код похожим на синхронный. Под капотом он преобразуется в цепочку промисов с использованием генераторов или похожих механизмов контроля выполнения.

Ключевые компоненты реализации

  1. Трансформация кода компилятором/интерпретатором Современные движки JavaScript (V8, SpiderMonkey) преобразуют async/await в состояние машины состояний (state machine). Каждое выражение await становится точкой приостановки и возобновления выполнения.

    Пример преобразования:

    // Исходный код
    async function fetchData() {
      const response = await fetch('/api/data');
      const data = await response.json();
      return data;
    }
    

    Приблизительный эквивалент на промисах:

    function fetchData() {
      return Promise.resolve()
        .then(() => fetch('/api/data'))
        .then(response => response.json());
    }
    
  2. Генераторы как основа (в ранних реализациях) Многие полифиллы используют генераторы для эмуляции async/await:

    // Полифил-подобная реализация через генераторы
    function asyncGenerator(generatorFunc) {
      return function(...args) {
        const generator = generatorFunc.apply(this, args);
        
        return new Promise((resolve, reject) => {
          function step(key, arg) {
            let result;
            try {
              result = generator[key](arg);
            } catch (error) {
              reject(error);
              return;
            }
            
            const { value, done } = result;
            
            if (done) {
              resolve(value);
            } else {
              return Promise.resolve(value)
                .then(val => step('next', val))
                .catch(err => step('throw', err));
            }
          }
          
          step('next');
        });
      };
    }
    
  3. Стек вызовов и микрозадачи Каждое await создает микрозадачу (microtask). Движок приостанавливает выполнение функции, освобождает основной поток, а когда промис разрешается, продолжает выполнение с этой точки через очередь микрозадач.

Что происходит при выполнении async-функции

  1. Создание и возврат промиса Async-функция всегда возвращает Promise, даже если внутри нет await:

    async function example() {
      return 42; // Автоматически оборачивается в Promise.resolve(42)
    }
    
  2. Приостановка и возобновление выполнения При встрече await:

    • Вычисляется значение справа от await
    • Если это не промис, оно преобразуется в промис
    • Выполнение функции приостанавливается
    • Управление возвращается в событийный цикл
    • Когда промис разрешается, функция продолжает выполнение
  3. Обработка ошибок try/catch внутри async-функций перехватывает как синхронные ошибки, так и отклоненные промисы:

    async function riskyOperation() {
      try {
        const result = await potentiallyFailingOperation();
        return result;
      } catch (error) {
        // Перехватит и синхронные ошибки, и rejected promise
        console.error('Operation failed:', error);
      }
    }
    

Особенности реализации в движках

  • V8 (Chrome, Node.js): Использует механизм TurboFan для оптимизации async/await, преобразуя в эффективный байт-код с минимальными накладными расходами
  • Скрытые классы и оптимизации: Движки создают внутренние структуры для быстрого переключения между состояниями приостановленных функций
  • Управление памятью: Замыкания и контекст выполнения сохраняются между приостановками, что требует аккуратного управления памятью

Практические следствия понимания внутреннего устройства

  1. Производительность: Каждый await создает микрозадачу. Иногда лучше выполнить несколько асинхронных операций параллельно:

    // Медленнее (последовательное выполнение)
    async function sequential() {
      const a = await task1();
      const b = await task2(); // Ждет завершения task1
      return a + b;
    }
    
    // Быстрее (параллельное выполнение)
    async function parallel() {
      const [a, b] = await Promise.all([task1(), task2()]);
      return a + b;
    }
    
  2. Отладка: Async-функции сохраняют стек вызовов через точки await, что упрощает отладку по сравнению с цепочками .then()

  3. Ошибки распространения: Необработанные исключения в async-функциях отклоняют возвращаемый промис, а не вызывают глобальные ошибки синхронно.

Итог: async/await — не магия, а продуманная абстракция над существующими асинхронными механизмами JavaScript. Понимание его внутреннего устройства помогает писать более эффективный и надежный асинхронный код, правильно использовать параллельное выполнение и избегать типичных ошибок управления потоком выполнения.