Что такое async и await в Python?
Что проверяют
Понимаете ли вы, что это кооперативная многозадачность в одном потоке, и знаете ли, чем её легко сломать.
Как отвечать
async def объявляет корутину: её вызов не выполняет тело, а возвращает объект корутины. Выполнением занимается цикл событий, обычно из asyncio.
await означает «здесь может быть ожидание, отдай управление циклу». Пока одна корутина ждёт ответа сети, цикл выполняет другие. Это не потоки: всё происходит в одном потоке, переключение кооперативное и только в точках await.
Основное из API: asyncio.run(main()) для точки входа, asyncio.create_task для параллельного запуска, asyncio.gather для ожидания нескольких результатов, asyncio.TaskGroup (с версии 3.11) как более безопасная замена gather с корректной отменой, asyncio.timeout для ограничения времени. Есть также асинхронные генераторы (async for) и контекстные менеджеры (async with).
Главное ограничение, ради которого вопрос и задают: выигрыш только на задачах ввода-вывода. Для вычислений это не помогает вовсе, потому что поток один, и глобальная блокировка интерпретатора тут ни при чём. Тяжёлые вычисления выносят в ProcessPoolExecutor.
Второе: один блокирующий вызов останавливает весь цикл. Обычный requests.get, time.sleep или синхронный драйвер базы внутри корутины замораживают все остальные задачи. Поэтому нужны асинхронные библиотеки (httpx, aiohttp, asyncpg), а неизбежное синхронное уводят через asyncio.to_thread.
Третье: вызов корутины без await ничего не выполняет, только создаёт объект и выдаёт предупреждение. И задача, созданная через create_task, на которую нигде не ждут, может быть собрана сборщиком мусора, поэтому ссылку на неё надо сохранять.
Где спотыкаются
Ждут ускорения вычислительного кода.
Вызывают синхронную библиотеку внутри корутины.
Забывают await и получают невыполненную корутину.
Запускают задачи циклом с await внутри вместо gather и теряют параллельность.