← Назад к вопросам

Как работает асинхронное программирование (async/await)?

2.0 Middle🔥 161 комментариев
#Python Core#Асинхронность и многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работает асинхронное программирование (async/await)

Базовая идея

Обычный синхронный код блокирует выполнение:

# Синхронно: ждём 2 секунды
result = requests.get("https://api.example.com/data")
print(result.json())
print("Готово")  # Выполнится только через 2 сек

Асинхронно позволяет выполнять другие задачи пока ждём:

# Асинхронно: запрос идёт в фоне
async def main():
    result = await fetch_data()
    print(result)
    print("Готово")

Синтаксис: async/await

async def my_async_function():
    print("Это async функция")
    await asyncio.sleep(1)
    return "Результат"

import asyncio
result = asyncio.run(my_async_function())
print(result)

Event Loop (циклик событий)

Это главный механизм:

import asyncio

async def task1():
    print("Task 1: start")
    await asyncio.sleep(2)
    print("Task 1: finish")

async def task2():
    print("Task 2: start")
    await asyncio.sleep(1)
    print("Task 2: finish")

async def main():
    await asyncio.gather(task1(), task2())

asyncio.run(main())
# Вывод: обе задачи бегут параллельно
# Всего 2 сек (не 3!)

Как это работает

Event loop - это циклик:

  1. Запускает задачу 1
  2. Задача 1 встречает await - отдаёт управление
  3. Event loop берёт задачу 2, запускает её
  4. Задача 2 встречает await - отдаёт управление
  5. Event loop проверяет: готовы ли данные для задачи 1?
  6. Когда готовы - запускает продолжение задачи 1

Ключь: когда задача ждёт (await), она отдаёт управление другим задачам.

Практические примеры

Множество HTTP запросов

import aiohttp
import asyncio

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.json()

async def main():
    urls = [
        "https://api.example.com/1",
        "https://api.example.com/2",
        "https://api.example.com/3",
    ]
    
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        return results

results = asyncio.run(main())
# Время: ~1 сек вместо 3 сек

Timeout и обработка ошибок

async def fetch_with_timeout():
    try:
        result = await asyncio.wait_for(
            fetch_data(),
            timeout=5.0
        )
        return result
    except asyncio.TimeoutError:
        print("Запрос слишком долгий")
        return None

asyncio.gather vs asyncio.wait

# gather: ждём все результаты
results = await asyncio.gather(task1(), task2(), task3())

# wait: ждём первые N завершённых
done, pending = await asyncio.wait(
    [task1(), task2(), task3()],
    return_when=asyncio.FIRST_COMPLETED
)

Важные отличия

СинхронныйАсинхронный
def func()async def func()
result = func()result = await func()
Блокирует процессПереключается между задачами
Простой тредОдин процесс, один тред

Когда использовать async

✅ Используй:

  • I/O операции (HTTP, БД, файлы)
  • Множество параллельных операций
  • Высокий параллелизм на одном сервере

❌ НЕ используй:

  • CPU-intensive задачи (нужны processes)
  • Простые скрипты без I/O
  • Когда нет асинхронной библиотеки

Частые проблемы

Забыл await

async def bad():
    result = fetch_data()  # НЕПРАВИЛЬНО!
    print(result)  # <coroutine object>

async def good():
    result = await fetch_data()
    print(result)  # Реальные данные

Смешивание sync и async

async def bad():
    time.sleep(2)  # БЛОКИРУЕТ event loop!

async def good():
    await asyncio.sleep(2)  # Не блокирует

Race conditions

counter = 0

async def bad_increment():
    global counter
    counter += 1  # Может быть race condition

lock = asyncio.Lock()

async def safe_increment():
    global counter
    async with lock:
        counter += 1  # Безопасно

FastAPI пример

from fastapi import FastAPI

app = FastAPI()

@app.get("/data")
async def get_data():
    result = await fetch_from_api()
    return {"data": result}

Производительность

Asyncio даёт преимущество только при I/O:

# 10 HTTP запросов по 1 сек каждый:
# Sync: 10 сек
# Async: ~1 сек (параллельно)

# CPU вычисления:
# Sync: 10 сек
# Async: 10 сек (всё равно на одном ядре)

Для CPU используй multiprocessing.

Итог

Async/await позволяет:

  • Запускать много I/O операций одновременно
  • На одинthread
  • Писать чистый код
  • Эффективно использовать ресурсы

Ключевые правила:

  • Event loop переключается между задачами на await
  • Никогда не блокируй (только async libraries)
  • Для CPU используй multiprocessing
  • ВСЕГДА await асинхронные функции
Как работает асинхронное программирование (async/await)? | PrepBro