Что произойдет при вызове асинхронной функции без await?
Что проверяют
Понимаете ли вы, что вызов корутинной функции ничего не запускает, и чем корутина отличается от задачи asyncio.Task. Следом обычно спрашивают, как запустить работу в фоне без ожидания и почему такая фоновая задача может молча пропасть.
Как отвечать
- что возвращается: вызов
fetch()безawaitсоздаёт объект корутины, и тело функции не выполняется ни на строчку; - что видно в логах: если объект так никто и не дождался, при сборке мусора Python выведет
RuntimeWarning: coroutine 'fetch' was never awaited. В режимеpython -X devasyncio работает в отладочном режиме и показывает, где корутина была создана; - как запустить без ожидания:
asyncio.create_task(fetch())ставит корутину в цикл событий, и она начнёт выполняться, когда текущий код дойдёт до ближайшегоawait. Для группы задач естьasyncio.gatherиasyncio.TaskGroup(Python 3.11+), который отменяет остальные задачи при ошибке одной; - ссылка на задачу: цикл событий держит на задачи только слабые ссылки, поэтому задача из
create_task, которую нигде не сохранили, может быть собрана посреди работы. Документация советует складывать задачи в множество и убирать черезtask.add_done_callback(tasks.discard); - ошибки: исключение в задаче, которую никто не дождался, не всплывёт, а попадёт в лог как
Task exception was never retrieved.
В реальном коде это выглядит так: в FastAPI забыли await session.commit() у AsyncSession из SQLAlchemy, эндпоинт вернул 200, а данных в базе нет. Для работы после ответа в FastAPI есть BackgroundTasks.
Где спотыкаются
Думают, что функция без await выполнится «в фоне», как в потоке. Она не выполнится вообще, пока её не дождутся или не обернут в задачу.
Пропускают await в условии: if has_access(user):. Объект корутины всегда истинный, проверка проходит у всех, и это уже дыра в безопасности. Забытый await в отдельной строке ловят mypy (код unused-coroutine) и pyright, а в условии его заметит только отдельно включённая проверка truthy-bool в mypy.
Используют create_task как «запустил и забыл» без сохранения ссылки и без обработки исключений. Задача то теряется, то падает молча.