Ошибки без await

Урок: Ошибки без await

Введение

Когда начинаешь работать с async / await, всё кажется простым: написал await, получил результат, работаешь дальше как с обычными данными.

Но есть важный момент, который часто упускают новички: что будет, если забыть await?

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

В программировании ситуация похожая. Если не дождаться выполнения асинхронной операции, ты начинаешь работать не с результатом, а с «обещанием» этого результата.

Это приводит к ошибкам, которые на первый взгляд выглядят странно и непонятно.


Что происходит без await

Рассмотрим простой пример:

function getData() {
  return Promise.resolve('Данные');
}

async function main() {
  let result = getData();
  console.log(result);
}

main();

Результат:

Promise { "Данные" }

Здесь мы ожидали увидеть "Данные", но получили Promise.

Почему так произошло:

  • getData() возвращает Promise;
  • мы не использовали await;
  • переменная result содержит Promise, а не значение.

Это одна из самых частых ошибок.


Правильный вариант с await

Добавим await:

function getData() {
  return Promise.resolve('Данные');
}

async function main() {
  let result = await getData();
  console.log(result);
}

main();

Теперь результат:

Данные

Разница в том, что:

  • await «разворачивает» Promise;
  • мы получаем реальное значение.

Ошибка при работе с результатом

Посмотрим более опасную ситуацию.

function getUser() {
  return Promise.resolve({ name: 'Stepan' });
}

async function main() {
  let user = getUser();
  console.log(user.name);
}

main();

Результат:

undefined

Почему:

  • user — это Promise;
  • у Promise нет свойства name;
  • поэтому user.nameundefined.

Исправим:

function getUser() {
  return Promise.resolve({ name: 'Stepan' });
}

async function main() {
  let user = await getUser();
  console.log(user.name);
}

main();

Теперь всё работает корректно.


Ошибки в цепочке вызовов

Проблема становится ещё серьёзнее, когда операций несколько.

function getUser() {
  return Promise.resolve({ name: 'Stepan' });
}

async function main() {
  let user = getUser();
  let name = user.name.toUpperCase();

  console.log(name);
}

main();

Здесь программа упадёт с ошибкой, потому что:

  • user — это Promise;
  • user.nameundefined;
  • у undefined нельзя вызвать toUpperCase().

Добавляем await:

function getUser() {
  return Promise.resolve({ name: 'Stepan' });
}

async function main() {
  let user = await getUser();
  let name = user.name.toUpperCase();

  console.log(name);
}

main();

Теперь всё работает правильно.


Ошибки с try/catch

Ещё одна важная проблема — обработка ошибок.

function getData() {
  return Promise.reject('Ошибка!');
}

async function main() {
  try {
    let result = getData();
  } catch (error) {
    console.log(error);
  }

  console.log('Без await отклонённый Promise не попадает в catch');
}

main();

Здесь ошибка не будет поймана в try/catch.

Почему:

  • getData() возвращает Promise;
  • ошибка происходит внутри Promise;
  • без await она не попадает в try/catch.

Исправим:

function getData() {
  return Promise.reject('Ошибка!');
}

async function main() {
  try {
    let result = await getData();
    console.log(result);
  } catch (error) {
    console.log(error);
  }
}

main();

Теперь ошибка будет обработана.


Логическая ошибка: код выполняется раньше времени

Иногда ошибка не приводит к падению, но ломает логику.

function getData() {
  return Promise.resolve({ value: 42 });
}

async function main() {
  let data = getData();

  console.log('Данные:', data);
}

main();

Результат:

Данные: Promise { ... }

Мы ожидали увидеть реальные данные, но получили Promise.

Это означает, что код продолжил выполнение раньше, чем данные были готовы.


Когда await не обязателен

Важно понимать: не всегда отсутствие await — ошибка.

Например:

function getData() {
  return Promise.resolve('Готово');
}

async function main() {
  let promise = getData();

  promise.then((data) => {
    console.log(data);
  });
}

main();

Здесь всё работает, потому что используется then.

Но смешивание then и await может запутать код, поэтому обычно выбирают один подход.


Где это проявляется на практике

Ошибки без await часто встречаются в реальных задачах:

  • при работе с API (fetch);
  • при загрузке данных;
  • при обработке форм;
  • при работе с базами данных.

Типичная ошибка — забыть await у fetch и у json():

// Неверно (оба выражения — Promise, не данные):
// let response = fetch('https://api.com/data');
// let data = response.json();

async function correctPattern() {
  let response = await Promise.resolve({
    json: async () => ({ ok: true }),
  });
  let data = await response.json();
  console.log(data);
}

correctPattern();

Здесь вместо сети показан учебный Promise.resolve с тем же интерфейсом, что у Response: два последовательных await — как в настоящем коде с fetch.


Итоговое понимание

Главная проблема без await в том, что ты работаешь не с результатом, а с Promise.

Это приводит к:

  • неправильным данным;
  • ошибкам доступа к свойствам;
  • неработающему try/catch;
  • нарушенной логике программы.

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

Если забыть его, код продолжит выполняться, как будто результат уже есть — и именно это становится источником большинства ошибок.