Futures и задачи в asyncio: создание, ожидание, отмена
Введение в Futures и задачи asyncio 🏁
В асинхронном Python ключевыми строительными блоками являются Futures и Tasks. Они позволяют эффективно управлять параллельным выполнением кода, не блокируя поток выполнения.
import asyncio
async def main():
# Создаем Future вручную
my_future = asyncio.Future()
print(f"Future готов? {my_future.done()}") # False
Futures представляют собой отложенные вычисления — это контейнеры для результата, который появится позже. Tasks — это подкласс Futures, обёртки вокруг корутин для их выполнения в event loop.
Создание задач: run_until_complete vs create_task ⚡
Есть два основных способа запуска корутин:
loop.run_until_complete()— запускает корутину и ждёт её завершенияloop.create_task()— планирует выполнение корутины без ожидания
async def fetch_data():
print("Начинаем загрузку данных...")
await asyncio.sleep(2)
return "Данные загружены"
# Способ 1
result = await fetch_data()
# Способ 2
task = asyncio.create_task(fetch_data())
🔑 Ключевое отличие: create_task позволяет запускать несколько операций параллельно, а run_until_complete работает последовательно.
Ожидание нескольких задач одновременно 🤹
Для эффективной работы с несколькими задачами используйте asyncio.gather() или asyncio.wait():
async def task_one():
await asyncio.sleep(1)
return "Результат 1"
async def task_two():
await asyncio.sleep(2)
return "Результат 2"
# Вариант 1: gather
results = await asyncio.gather(task_one(), task_two())
# Вариант 2: wait
tasks = [task_one(), task_two()]
done, pending = await asyncio.wait(tasks)
📌 gather возвращает результаты в порядке вызова, а wait — множества завершённых и ожидающих задач.
Отмена задач и таймауты ⏳
В реальных приложениях часто требуется: 1. Прерывать долгие операции 2. Задавать временные ограничения
async def long_operation():
try:
await asyncio.sleep(10)
return "Успех"
except asyncio.CancelledError:
print("Операция отменена!")
raise
# Создаем и отменяем задачу
task = asyncio.create_task(long_operation())
await asyncio.sleep(1)
task.cancel()
# Таймаут операции
try:
result = await asyncio.wait_for(long_operation(), timeout=2.0)
except asyncio.TimeoutError:
print("Время истекло!")
🚨 Важно: Всегда корректно обрабатывайте CancelledError в своих корутинах!
Практический пример: параллельные HTTP-запросы 🌐
Давайте соберём всё вместе в реалистичном сценарии:
import aiohttp
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
urls = [
'https://python.org',
'https://yandex.ru',
'https://google.com'
]
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
pages = await asyncio.gather(*tasks, return_exceptions=True)
for url, content in zip(urls, pages):
print(f"{url}: {len(content)} символов")
asyncio.run(main())
🔥 Что происходит:
- Создаётся сессия HTTP-клиента
- Запускаются параллельные задачи для каждого URL
- Результаты собираются автоматически
- Обрабатываются возможные ошибки (
return_exceptions=True)
Продвинутые техники работы с Tasks 🎯
- Отслеживание прогресса: можно использовать callback-функции через
add_done_callback - Ограничение параллелизма: с помощью семафоров (
asyncio.Semaphore) - Обработка исключений: через метод
exception()у Task
async def monitored_task():
await asyncio.sleep(1)
if random.random() > 0.5:
raise ValueError("Что-то пошло не так!")
def callback(future):
if future.exception():
print(f"Ошибка в задаче: {future.exception()}")
task = asyncio.create_task(monitored_task())
task.add_done_callback(callback)
💡 Для более сложных сценариев рекомендую курс Данилы Бежина по asyncio на YouTube: https://www.youtube.com/@DanilaBezhin
Заключение и главные выводы 🏆
- Tasks — обёртки вокруг корутин для параллельного выполнения
- Futures — низкоуровневые объекты для отложенных вычислений
- Используйте
gatherдля параллельного выполнения с сохранением порядка - Всегда предусматривайте механизмы отмены и таймаутов
- Обрабатывайте исключения на уровне отдельных задач и всего приложения
Теперь вы готовы эффективно использовать асинхронность в Python! Попробуйте применить эти знания в своих проектах — результат вас впечатлит. 🚀