Async — это не про скорость, а про ожидание

Автор: Артём Шумейко · YouTube

Артём Шумейко — Python-разработчик и преподаватель, специализируется на backend. В коротком видео показывает на живом примере и с реальными цифрами, как FastAPI обрабатывает больше тысячи запросов в секунду.

Асинхронность часто объясняют через «это быстро» — и это неточная формулировка, которая потом мешает. FastAPI не делает CPU-код быстрее. Он устраняет простой: пока один запрос ждёт ответа от базы данных или внешнего сервиса, event loop берёт следующий. При I/O-bound нагрузке это даёт кратный прирост пропускной способности без дополнительных потоков и без накладных расходов на переключение контекста. Видео полезно тем, что показывает разницу измеримо, а не концептуально.

Кому смотреть: Python-разработчикам, которые пишут async/await по инерции или «потому что так принято в FastAPI», но не могут объяснить, почему это работает и когда это не поможет.

Из этого можно взять в работу: найди в своём коде async-функцию, которая делает блокирующий вызов — requests.get() вместо httpx, time.sleep() вместо asyncio.sleep(). Одна такая точка блокирует весь event loop.

Центральная механика видео — демонстрация разницы между синхронным и асинхронным подходом под нагрузкой. FastAPI построен на Starlette и запускается через Uvicorn (ASGI-сервер), что принципиально отличает его от Flask с Gunicorn: там каждый воркер-процесс обрабатывает один запрос за раз, здесь event loop переключается между задачами в момент ожидания I/O.

Суть asyncio: когда функция встречает await, она сигнализирует event loop — «я жду, можешь заняться другим». Event loop берёт следующую корутину из очереди. Это кооперативная многозадачность: потоки не плодятся, переключения дешёвые, накладные расходы минимальны.

Важный нюанс, который часто упускают: async помогает только с I/O-bound задачами. Если функция считает числа без ожиданий — async ничего не даст, а лишний await добавит только overhead. Для CPU-bound нагрузки нужны процессы (multiprocessing) или другие инструменты.

Отдельная ловушка — блокирующие библиотеки внутри async-кода. await asyncio.sleep(1) отпускает event loop продолжать работу. time.sleep(1) замораживает весь event loop на секунду, и все параллельные запросы стоят. Это частая ошибка при переходе на async, которую сложно поймать без нагрузочного теста.