Асинхронные функции: async def, await и работа с корутинами

🔄 Синхронный vs Асинхронный Python: зачем вообще это нужно?

Представьте, что ваш код — это повар в ресторане. Синхронный повар готовит один заказ за раз: варит суп, ждёт пока он сварится, потом жарит стейк, опять ждёт... Асинхронный же повар начинает варить суп, пока тот кипит — переключается на стейк, и так управляет десятком блюд одновременно!

Проблема синхронного кода:

import time

def make_coffee():
    time.sleep(3)  # Ждём 3 секунды
    return "Кофе готов!"

def make_toast():
    time.sleep(2)  # Ждём 2 секунды
    return "Тосты готовы!"

# Общее время выполнения: 5 секунд!
breakfast = [make_coffee(), make_toast()] 

🐍 Корутины: сердце асинхронности

Корутина (coroutine) — это функция, которая может приостанавливать своё выполнение и передавать управление другим корутинам. В Python они создаются через async def.

Базовый пример:

import asyncio

async def make_coffee():
    print("Начинаем варить кофе...")
    await asyncio.sleep(3)  # Не блокируем поток!
    return "Кофе готов!"

# Запуск корутины
coffee = make_coffee()
print(coffee)  # <coroutine object make_coffee at 0x...> 

Важные моменты:

  • Корутина не выполняется сразу при вызове, а возвращает coroutine-объект
  • Для запуска нужен событийный цикл (event loop)
  • await — ключевое слово для ожидания результатов других корутин

⚡ await: волшебный ключ к асинхронности

Оператор await делает две важные вещи:

  1. Приостанавливает выполнение текущей корутины
  2. Передаёт управление обратно в событийный цикл

Правила использования:

  • await можно использовать только внутри async def
  • Объект после await должен быть "awaitable" (корутина, Task, Future и т.д.)
async def breakfast():
    coffee_task = asyncio.create_task(make_coffee())
    toast_task = asyncio.create_task(make_toast())

    # Параллельное выполнение!
    coffee = await coffee_task
    toast = await toast_task

    print(f"{coffee} и {toast} - завтрак подан!")

🛠 Практика: делаем асинхронный HTTP-запрос

Давайте попробуем реальный пример с библиотекой aiohttp:

import aiohttp
import asyncio

async def fetch_data(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

async def main():
    urls = [
        'https://python.org',
        'https://github.com',
        'https://habr.com'
    ]

    tasks = [fetch_data(url) for url in urls]
    results = await asyncio.gather(*tasks)

    for url, content in zip(urls, results):
        print(f"{url}: {len(content)} символов")

# Запускаем событийный цикл
asyncio.run(main())

Что происходит:

  1. Создаются задачи для каждого URL
  2. asyncio.gather запускает их параллельно
  3. Мы получаем все результаты, когда они готовы

💡 Важные нюансы работы с корутинами

  1. Не смешивайте sync и async код — это может заблокировать event loop
  2. Используйте специальные async-библиотеки (aiohttp, asyncpg, aioredis)
  3. Обрабатывайте ошибки через try/except в корутинах
  4. Ограничивайте параллелизм с помощью семафоров:
semaphore = asyncio.Semaphore(5)  # Максимум 5 одновременных запросов

async def limited_fetch(url):
    async with semaphore:
        return await fetch_data(url)

🚀 Главные преимущества асинхронности

  1. Экономия времени — параллельное выполнение I/O операций
  2. Масштабируемость — тысячи одновременных соединений
  3. Реактивность — приложения не "зависают" на долгих операциях
Скрыть рекламу навсегда

📘 VK Видео — обучение без ограничений

Все уроки доступны без VPN, без блокировок и зависаний.

Можно смотреть с телефона, планшета или компьютера — в любое время.

▶️ Смотреть на VK Видео