Короткий ответ
Обычный вызов функции выполняется сразу и полностью в текущем потоке управления; await на корутине передаёт управление циклу событий, позволяя другим задачам выполняться во время ожидания.
Как это работает подробнее
Вызов foo() — прямая передача управления в её тело и возврат результата по завершении, без возможности «уступить» выполнение чему-то ещё. await coro — явная точка приостановки: пока корутина ждёт (например, I/O), цикл может переключиться на другие готовые корутины.
Пример кода
example-258.py
import asyncio
async def slow_io():
await asyncio.sleep(1)
return "data"
async def main():
coro = slow_io() # ничего не выполнилось, просто создан объект корутины
print(type(coro)) # <class 'coroutine'>
result = await coro # вот теперь тело выполняется, с возможностью переключения цикла
print(result)
asyncio.run(main())Что использовать на практике
- Практика.
awaitможно использовать только внутриasync defфункции; обращение к корутине безawaitне выполняет её тело — создаётся объект корутины, который нужно либоawait-нуть, либо передать вasyncio.create_task/gather.
Подводные камни
- Риски. Пытаются использовать
awaitвне async-функции (SyntaxError); ожидают, чтоawaitсам по себе даёт параллелизм для CPU-bound кода — выгода есть только если ожидаемая операция реализует неблокирующее ожидание.