Почему aiogram переписывают с асинхронного на синхронный код
Что проверяют
Проверяют не знание конкретной библиотеки, а способность не проглотить предпосылку. aiogram построен на asyncio, и «переписывание на синхронный код» это либо осознанное решение команды, либо симптом неверно понятой проблемы. Смотрят, уточните ли вы контекст и понимаете ли, где асинхронность реально даёт выигрыш.
Как отвечать
Скажите прямо: бот это задача, где почти всё время уходит на ожидание сети, и asyncio здесь уместен. Причины, по которым команда всё же уходит в синхронный код, бывают такие: весь остальной стек синхронный (драйвер базы, клиент HTTP, устоявшийся слой моделей), и асинхронность красит вокруг себя весь код; трассировки и отладка проще; нагрузка невелика, и выигрыш от совмещения ожиданий не окупает сложности.
Гораздо чаще причина другая: в обработчик попал блокирующий вызов. Один такой вызов останавливает весь цикл событий, бот перестаёт отвечать всем сразу, и делается вывод «асинхронность не работает». Лечится это не сменой парадигмы, а выносом блокирующего кода:
@router.message()
async def handler(msg: Message) -> None:
rows = await asyncio.to_thread(legacy_sync_query) # цикл событий не блокируется
await msg.answer(render(rows))Хороший финал: сначала измерить, на чём стоит время. Долгие вычисления вообще не место в обработчике, их выносят в отдельный процесс или очередь задач, а ожидание сети оставляют асинхронным.
Где спотыкаются
Принимают формулировку вопроса за факт и начинают доказывать, что асинхронность вредна. Уточняющий вопрос здесь сильнее заготовленного ответа.
Считают, что async def сам по себе даёт параллельность. Поток один, цикл один, и time.sleep или тяжёлый проход по данным останавливают обработку сразу у всех пользователей.
Смешивают два мира наугад: зовут asyncio.run внутри уже работающего цикла, забывают await и получают корутину вместо результата, заворачивают в поток то, что и так асинхронно.