В асинхронном программировании принято говорить, что объект является awaitable, то есть это объект, который, в какой-то момент времени может ничего полезного не делать, а заниматься только ожиданием каких-то результатов от сторонних сервисов (например ответа сервера с результатами на свой запрос). Такие объекты всегда запускаются с оператором await
.
Практически все API-интерфейсы модуля asyncio
предназначены для приема awaitable объектов - объектов ждущих каких-то результатов или команд на продолжение или приостановку работы от основного цикла событий.
Есть три основных типа объектов ожидания awaitable
, которые можно запускать оператором await
в асинхронном коде:
async
.Сопрограммы в Python являются объектами, которые могут ждать результатов от сторонних сервисов или своей очереди выполнения, следовательно, их можно также использовать с оператором wait
внутри других сопрограмм:
import asyncio async def nested(): return 42 async def main(): # Ничего не происходит, если мы просто вызываем "nested()". # Сопрограмма `nested()` создается, но не будет выполняться, # т.к. в таком виде она заблокирует цикл событий, что недопустимо nested() # что бы асинхронная функция `nested()` заработала # необходимо заставить ее ждать своего выполнения # при помощи оператора `await` print(await nested()) asyncio.run(main()) # 42
Важно. Здесь термин "сопрограмма" может использоваться для двух тесно связанных понятий:
*async def*
;Примечание:
Модуль asyncio
также поддерживает устаревшие сопрограммы на основе генератора. Поддержка сопрограмм на основе генераторов запланирована к удалению в Python 3.10.
Task
.Задачи (asyncio.Task
) используются для одновременного планирования запуска нескольких сопрограмм. Когда сопрограмма оборачивается в задачу (передается в функцию asyncio.create_task()
), то сопрограмма будет автоматически запускаться в ближайшее время, как только будет это возможным:
import asyncio async def nested(): return 42 async def main(): # Запланируем запуск 'nested()' в одновременно с 'main()'. task = asyncio.create_task(nested()) # объект 'task' может теперь использоваться, для отмены # выполнения 'nested()' или ожидания ее выполнения: await task asyncio.run(main()) # 42
Запуск задачи всегда происходит оператором await
, т. к. она будет ждать своего выполнения.
Future
объект - инкапсулирует асинхронное выполнение вызываемого объекта и представляет специальный низкоуровневый объект, который хранит промежуточное состояние запущенной задачи (когда она что-то ожидает) и в будущем, будет представлять конечный результат асинхронной операции. Этот объект может хранить информацию о том, что задача ещё не выполнена или не до конца выполнена, или может хранить уже полученный результат, или исключение, полученное во время выполнения кода.
Когда происходит ожидание объекта Future
, это означает, что сопрограмма будет ждать, пока Future
не будет разрешен в каком-то другом месте.
Futures
объекты позволяют использовать код на основе обратного вызова (который сообщает о готовности объекта Future
) совместно с синтаксисом async/await
, по этому они необходимы в асинхронном программировании.
Нет необходимости создавать объекты Future
на уровне приложения. В основном, эти объекты создаются автоматически, при вызове функций или методов, предоставляемыми асинхронными API модулей:
async def main(): await function_that_returns_a_future_object() # это тоже правильно: await asyncio.gather( function_that_returns_a_future_object(), some_python_coroutine() )
Хорошим примером низкоуровневой функции, возвращающей Future
объект, является функция loop.run_in_executor()
.