> Что произойдет при вызове асинхронной функции без 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 на задаче.
Пример кода
PYTHONimport asyncioasync def fetch_data():print("Начало fetch_data")await asyncio.sleep(1)print("Конец fetch_data")return 42async 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 taskprint(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. - Неупоминание того, что исключения внутри корутины теряются без следа.
> Похожие задачи по Python
Что такое контейнеризация и зачем она нужна
Для чего нужны фикстуры в pytest
Что такое Depends в FastAPI и для чего он нужен
Когда переходить на микросервисы
> Похожие задачи по backend
Что такое контейнеризация и зачем она нужна
Для чего нужны фикстуры в pytest
Что такое Depends в FastAPI и для чего он нужен
Когда переходить на микросервисы
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью