Для каких задач подходит асинхронный код в Python?
Что проверяют
Отличаете ли вы I/O-bound задачи от CPU-bound и понимаете ли, что asyncio не делает код параллельным. Следом обычно спрашивают, что будет, если внутри корутины вызвать requests.get или time.sleep, и чем asyncio отличается от потоков.
Как отвечать
Асинхронный код в Python это кооперативная многозадачность в одном потоке. Цикл событий переключает корутины в точках await: пока одна ждёт сеть или диск, работают другие. Выигрыш появляется, когда задача большую часть времени ждёт:
- много сетевых запросов: сотни вызовов API или скачиваний через
aiohttpилиhttpx.AsyncClientодновременно, с ограничением черезasyncio.Semaphore; - серверы с большим числом соединений: FastAPI и Starlette под uvicorn, WebSocket, чаты, long polling, где тысячи соединений висят в ожидании;
- базы и очереди:
asyncpg, асинхронный режим SQLAlchemy, клиенты Redis и Kafka; - боты и интеграции: aiogram для Telegram, парсеры и краулеры.
Для CPU-bound задач асинхронность ничего не даёт: тяжёлый расчёт в корутине просто остановит цикл событий. Такое выносят в процессы через ProcessPoolExecutor или multiprocessing, а блокирующий вызов, для которого нет асинхронной библиотеки, в поток через asyncio.to_thread.
Из современного стоит назвать asyncio.TaskGroup и asyncio.timeout() из Python 3.11. Это структурированная конкурентность: ошибка в одной задаче отменяет остальные, а голый gather оставляет их работать.
Где спотыкаются
Вызывают блокирующий код внутри async def: requests, time.sleep, синхронный драйвер БД. Сервис формально асинхронный, а на деле обрабатывает запросы по одному.
Считают, что async ускоряет любой код. Один запрос к базе быстрее не станет, выигрыш только в том, что за время ожидания обслуживаются другие.
Объявляют эндпоинт FastAPI через async def и зовут внутри синхронную библиотеку. Обычную def FastAPI отправит в пул потоков, и цикл событий не пострадает, а async def с блокирующим вызовом заблокирует его целиком.