Python async / await Обяснено: Урокът за 2026 г
Async в Python има репутацията на объркващ. Не е задължително да е така. След като интернализирате едно изречение — async позволява на ЕДНА нишка да изпълнява множество I/O-обвързани задачи, преплетени — всичко останало следва.
Този урок обхваща: какво всъщност правят async def и await, кога async печели срещу това, когато е грешен инструмент, четирите модела, които всяка база от асинхронни кодове използва (последователно, събиране, семафор, изчакване) и трите начина, по които начинаещите мълчаливо преглъщат грешките.
Мисловният модел с едно изречение
Async в Python е кооперативна паралелност в една нишка. Когато вашият код удари await, управлението се връща към цикъла на събитията, който може да изпълнява друга задача, докато първата чака I/O. Когато I/O завърши, цикълът възобновява първата задача от мястото, където е на пауза.
Това е. Без нишки, без процеси, без паралелизъм (в смисъла на CPython-with-GIL). Само една нишка жонглира с много задачи, които прекарват по-голямата част от времето си в чакане в мрежа/диск/база данни.
Async е по-бърз, когато вашето тясно място чака, а не изчисли.
Триредовата минимално жизнеспособна асинхронна програма
async def hello()— декларира коррутина. Извикването наhello()не изпълнява кода; той създава сърутинен обект.await asyncio.sleep(1)— връща контрола обратно на цикъла за събития за 1 секунда.asyncio.run(hello())— създава цикъл на събитието и изпълнява съпрограмата до завършване.
Правило: `await` само вътре в `async def`. Извикването на await в обикновена функция е SyntaxError.
Когато async печели (и когато не)
Async печели: I/O свързана работа
Всяка задача, която прекарва по-голямата част от времето си на стенен часовник В ЧАКАНЕ, е кандидат за async:
- HTTP заявки (външни API, скрапинг, уеб кукички)
- Заявки към бази данни
- Файлът чете / пише
- WebSocket / gRPC потоци
- Консуматори на опашка (Redis, RabbitMQ, Kafka)
Пример: извличане на 100 URL адреса.
50-100x ускоряване на стенния часовник за I/O-тежка работа. Това е мястото, където async печели своя бюджет за сложност.
Async ГУБИ: работа, свързана с процесора
Async не ви дава паралелизъм за изчисление. Тъй като е кооперативен еднопоточен, горещият цикъл на процесора блокира целия цикъл на събитията.
Всички други задачи умират, докато pbkdf2 работи. За работа, свързана с процесора, посегнете към multiprocessing или concurrent.futures.ProcessPoolExecutor, а не към async.
Смесено натоварване: run_in_executor
Ако ЕДНА стъпка е натоварена с CPU, а останалата част е I/O, обвийте CPU стъпката, така че да работи в пул от нишки, без да блокира цикъла:
Няма = набор от нишки по подразбиране. Страхотно за извикване на библиотеки за синхронизиране на трети страни, които са бавни, но не можете да пренапишете.
Четирите модела
1. Последователно — една задача наведнъж
Използвайте, когато всяка заявка зависи от резултата на предишната.
2. asyncio.gather — запалва всички наведнъж
Избор по подразбиране за независими I/O задачи. Но: ако съберете 10 000 задачи срещу един API, ще получите ограничени по скорост или изчерпателни файлови дескриптори. Тук се намесва моделът на семафора.
3. Семафор — ограничение на паралелността
asyncio.Semaphore(N) позволява най-много N задачи след async with sem: във всеки момент. Опашката с други задачи. Използвайте за всеки външен API с ограничение на скоростта или база данни с набор от връзки.
4. Изчакване — изоставете бавната работа
asyncio.wait_for отменя основната задача след N секунди. От съществено значение за всеки асинхронен код, който говори с мрежа - никога не правете неограничено изчакване.
Трите капана за преглъщане на грешки
Прихващане 1: asyncio.gather с return_exceptions=True
Полезно, когато искате да запазите частични резултати, но СМЪРТОНОСНО, ако не проверите всеки резултат:
Без проверка грешките тихо изчезват.
Капан 2: Запалете и забравете задачи
Ако background_work() се повдигне, Python издава предупреждение при изключване и продължава. За всичко, което ви интересува, запазете задачата и я изчакайте или използвайте asyncio.TaskGroup (3.11+), който разпространява всички грешки.
Прихващане 3: Код за синхронизиране вътре в async
заявки е синхронно. Дори вътре в async def, requests.get блокира целия цикъл, докато пристигне отговорът. Използвайте „httpx“ (или „aiohttp“) за асинхронен HTTP.
Правило: никога не използвайте синхронна I/O библиотека в асинхронен код. Ако библиотеката няма асинхронен вариант, обвийте я в run_in_executor.
asyncio.TaskGroup — 2026 начин за разпръскване
Python 3.11 добави TaskGroup, който коригира чисто проблема с запалването и забравянето:
Структурирана паралелност — без изгубени грешки, без осиротели задачи. Предпочитайте това пред голото create_task в съвременния код.
Пример от реалния свят: скрепер с ограничена скорост
Всички четири модела в 15 реда: ограничена паралелност (семафор), изчакване (wait_for), изрични разклонения за грешка (без преглъщане) и събиране за разклоняване. Копирайте и поставете тази форма във всяка асинхронна I/O задача.
Async в Python има истинска крива на обучение, но менталният модел е прост: една нишка, кооперативни паузи при await, I/O се припокриват. Овладейте gather + Semaphore + wait_for + TaskGroup и ще сте покрили 95% от асинхронната работа, която някога ще пишете.
Следваща стъпка: Senior Python track има 20 практически урока за asyncio, TaskGroup, aiohttp/httpx и моделите за едновременност по-горе — с истински Python, работещ във вашия браузър.