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

Что происходит в браузере когда код доходит до await?

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

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

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

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

Подробное объяснение работы await в браузере

Когда код JavaScript в браузере доходит до ключевого слова await, происходит последовательность важных событий, которые можно разделить на несколько этапов. Важно понимать, что await работает только внутри async-функций и тесно связан с механизмом Event Loop и микрозадач (microtasks).

1. Приостановка выполнения async-функции

async function fetchData() {
    console.log('Начало функции');
    const response = await fetch('https://api.example.com/data'); // Точка await
    const data = await response.json(); // Еще один await
    console.log('Данные получены:', data);
    return data;
}

console.log('До вызова функции');
fetchData().then(result => console.log('Функция завершена'));
console.log('После вызова функции');

Что происходит на строке с await:

  • Выполнение текущей async-функции приостанавливается в точке await
  • Управление возвращается в вызывающий контекст (то, что вызвало async-функцию)
  • Функция не блокирует основной поток - браузер остается отзывчивым

2. Обработка Promise и создание микрозадачи

// Под капотом примерно так:
const promise = fetch('https://api.example.com/data'); // 1. Promise создается
// 2. Движок добавляет обработчик .then() к Promise
promise.then(
    (result) => {
        // 3. Когда Promise выполнится, это станет микрозадачей
        // 4. Выполнение продолжается с этого места
    }
);

Когда await встречает Promise, происходит:

  • JavaScript "распаковывает" Promise
  • Если Promise уже выполнен, значение возвращается сразу
  • Если Promise находится в состоянии pending, функция приостанавливается
  • Движок неявно подписывается на завершение Promise через .then()

3. Освобождение стека вызовов и Event Loop

Event Loop продолжает работать даже во время ожидания:

  • Макрозадачи (macrotasks): события мыши, таймеры, рендеринг
  • Микрозадачи (microtasks): промисы, queueMicrotask, MutationObserver
// Демонстрация порядка выполнения
async function demo() {
    console.log('1: Начало async функции');
    
    await Promise.resolve(); // Микрозадача 1
    
    console.log('3: После await (выполнится как микрозадача)');
}

console.log('0: До вызова');
demo();
console.log('2: После вызова');

// Вывод:
// 0: До вызова
// 1: Начало async функции
// 2: После вызова
// 3: После await

4. Возобновление выполнения

Когда Promise разрешается (resolves):

  • Обработчик завершения помещается в очередь микрозадач
  • Event Loop доходит до этой микрозадачи после завершения текущей синхронной задачи
  • Выполнение async-функции возобновляется с места остановки
  • Значение Promise становится результатом выражения await

5. Внутренняя механика: генераторы и состояния

Под капотом async/await реализован на основе генераторов:

// Упрощенная аналогия с генераторами
function* generatorVersion() {
    const response = yield fetch('https://api.example.com/data');
    const data = yield response.json();
    return data;
}

// Async/await делает это автоматически:
// 1. Сохраняет контекст выполнения
// 2. Запоминает точку остановки
// 3. Управляет состоянием функции

6. Особенности для разных типов значений

await работает не только с Promise:

async function handleDifferentValues() {
    // 1. Если значение - не Promise, оно преобразуется в resolved Promise
    const num = await 42; // Promise.resolve(42)
    
    // 2. Если Promise отклоняется (rejects) - выбрасывается исключение
    try {
        await Promise.reject(new Error('Ошибка'));
    } catch (error) {
        console.log('Ошибка перехвачена:', error.message);
    }
    
    // 3. Thenable объекты также работают
    const thenable = {
        then: function(resolve, reject) {
            setTimeout(() => resolve('thenable'), 100);
        }
    };
    const result = await thenable;
}

7. Влияние на производительность и рендеринг

Критически важно понимать:

  • await не блокирует основной поток браузера
  • Анимации и интерактивность продолжают работать
  • Рендеринг страницы не прерывается
  • Однако, код после await в той же функции выполнится только когда:
    • Текущая задача стека вызовов завершена
    • Все микрозадачи перед ней выполнены
    • Браузер готов к выполнению JavaScript

Практический пример с таймерами

async function multipleAwaits() {
    console.time('Общее время');
    
    // Параллельное выполнение (оптимальный подход)
    const [user, posts] = await Promise.all([
        fetch('/api/user'),
        fetch('/api/posts')
    ]);
    
    // Последовательное выполнение (может быть медленнее)
    const userData = await user.json();     // Ждем завершения
    const postsData = await posts.json();   // Ждем завершения
    
    console.timeEnd('Общее время');
    return { userData, postsData };
}

Заключение

Ключевой принцип: await приостанавливает выполнение ТОЛЬКО текущей async-функции, но не блокирует Event Loop браузера. Это позволяет писать асинхронный код, который выглядит как синхронный, сохраняя при этом отзывчивость интерфейса. Понимание этой механики помогает избежать распространенных ошибок, таких как непреднамеренное последовательное выполнение параллельных операций или блокировка рендеринга страницы.

Что происходит в браузере когда код доходит до await? | PrepBro