> Что произойдет при вызове асинхронной функции без await? (Python)

Уровень: senior · Роль: backend · Язык: Python · Категория: Технические вопросы

Компании: Notamedia, Точка

Стек: Python

> Пример ответа

Короткий ответ

При вызове асинхронной функции без await вы получаете объект корутины (coroutine object), но сама функция не выполняется. Это не ошибка, но корутина остаётся невыполненной и будет уничтожена сборщиком мусора с предупреждением RuntimeWarning: coroutine was never awaited. Если корутина создаёт ресурсы (открывает соединения, файлы) - они не будут освобождены корректно.

Подробное объяснение

Асинхронная функция в Python - это корутина. При вызове она не выполняет тело, а лишь создаёт объект корутины, который нужно запустить через await, asyncio.run(), asyncio.create_task() или asyncio.gather(). Без await корутина попадает в состояние "не запущена" и никогда не выполнится.

Ключевые моменты:

  • Создание корутины - это не вызов функции. Тело корутины начнёт выполняться только при первом await или при передаче в event loop.
  • Предупреждение RuntimeWarning появляется при сборке мусора, если корутина не была await'нута. В Python 3.12+ это предупреждение стало более заметным, а в некоторых окружениях (например, pytest с asyncio_mode=auto) может превращаться в ошибку.
  • Утечка ресурсов: если корутина внутри открывает файл, соединение или транзакцию, и вы её не await'нули - эти ресурсы не будут закрыты. Это особенно опасно в долгоживущих процессах (веб-серверах, воркерах).
  • Исключения внутри корутины не будут пойманы - они просто потеряются, что затрудняет отладку.
  • Порядок выполнения: если вы вызвали корутину без await и затем вызвали await на другой корутине, первая так и останется невыполненной - event loop не знает о её существовании.

На практике

В реальном коде это часто происходит по невнимательности:

  • Забыли await перед вызовом асинхронной функции.
  • Передали корутину в функцию, которая ожидает обычный callable (например, в threading.Thread или functools.partial).
  • Вызвали корутину внутри синхронной функции без asyncio.run().

Для отладки полезно включить asyncio.get_event_loop().set_debug(True) - тогда предупреждение появится сразу, а не при сборке мусора.

Если корутина создаёт ресурсы, лучше явно обрабатывать её жизненный цикл: использовать async with, try/finally или передавать в asyncio.create_task() с последующим await на задаче.

Пример кода

PYTHON
import asyncio
async def fetch_data():
print("Начало fetch_data")
await asyncio.sleep(1)
print("Конец fetch_data")
return 42
async def main():
# Ошибка: корутина создана, но не await'нута
coro = fetch_data()
print("Корутина создана, но не выполнена")
# Предупреждение появится при сборке мусора
# RuntimeWarning: coroutine 'fetch_data' was never awaited
# Правильно:
result = await fetch_data()
print(f"Результат: {result}")
# Или через create_task:
task = asyncio.create_task(fetch_data())
result2 = await task
print(f"Результат через task: {result2}")
asyncio.run(main())

Как отвечать на собеседовании

Начните с короткого ответа: "Вызов асинхронной функции без await возвращает объект корутины, но не выполняет её". Затем поясните, что корутина - это ленивый объект, и без await она не попадает в event loop. Упомяните RuntimeWarning и риск утечки ресурсов. Если спросят про исключения - скажите, что они теряются. Хорошо добавить пример, как это проявляется в реальном коде (забытый await в обработчике запроса).

Что проверяет интервьюер

  • Понимание модели выполнения asyncio: корутины - это не функции, а объекты.
  • Знание жизненного цикла корутины и event loop.
  • Осознание последствий: предупреждения, утечки, потеря исключений.
  • Умение объяснить разницу между await, create_task и asyncio.run().
  • Внимательность к деталям: знание про RuntimeWarning и debug-режим.

Типичные ошибки

  • Утверждение, что "функция просто не выполнится" - это не ошибка, но недостаточно: нужно объяснить, что корутина создаётся, и что с ней происходит.
  • Путаница между корутиной и asyncio.Task - task создаётся только через create_task, а не при вызове функции.
  • Игнорирование предупреждения: некоторые считают, что это безобидно, но в production это может привести к серьёзным утечкам.
  • Предложение "просто обернуть в asyncio.run()" - это не всегда правильно, особенно внутри уже работающего event loop.
  • Неупоминание того, что исключения внутри корутины теряются без следа.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью