TGViewer
Советы разработчикам (python и не только) Советы разработчикам (python и не только) @advice17 · 8.35K subscribers
Post #71 15.9K
Asyncio и цикл событий

Чтобы понять, как рабоатет asyncio, давайте рассмотрим, как в Python работают функции.

Мы можем вызвать функцию, она выполнится. Просто интерпретатор берет её код и выполняет целиком. Если же мы берём функцию-генератор и просто вызываем, ничего не будет происходить. Тебе надо по ней пробежаться циклом или самому вызывая next. Когда мы вызываем next, код функции доходит до следующего yield и останавливается.

Представим, что у нас есть основной части программы цикл, который крутит этот генератор.

def foo():
yield 1
yield 2

for _ in foo():
pass

Если между yield вызывается обычная функция, то она выполняется как обычно, и не важно, что это происходит внутри генератора. А что, если наш генератор внутри себя вызывает другой генератор через yield from?

def foo():
yield 1
yield 2

def bar():
yield from foo()

for _ in bar():
pass

В этом случае, наш основной цикл просто крутится дальше как раньше, но уже выполняется не код первой функции, а переходит к той, что она вызвала и останавливается уже на её yield. То есть мы как бы крутим уже вложенный генератор, а не оригинальный. Нашему циклу не важно, откуда берутся шаги внутри функции: то ли она сама их выдумала, то ли получила из вложенных вызовов.

Проводя аналогию с asyncio наш главный цикл - это event loop, а генераторы - корутины. Корутины могут друг друга вызвать и циклу норм, он продолжает их крутить. Разница чисто синтаксическая: в asyncio используется await вместо yield.

Для полнцоенного использования тут не хватает одной важной детали.

На текущий момент у нас есть цикл, который постоянно дёргает генератор и при завершении шага переходит сразу к следующему. В реальности же мы хотим приостанавливать выполнение до получения данных из сети, наступления определенного времени и т.п. В норме мы хотим отдать управление циклу событий, чтобы он там что-то ещё мог поделать своё пока мы ждем. Но, как только слип завершится или сокет будет готов, надо вернуться назад и продолжить выполнение.

Итого, мы крутим цикл. Там генераторы, вызывающие генераторы. Мы это все крутим, крутим. Получаем оттуда объект, который наш loop умеет ожидать (Future). И каждый раз, прежде чем крутить дальше, ждём что он будет готов.

А раз нас есть цикл, который умеет ждать между вызовами next, он может в это время дёрнуть другой генератор, если у него есть их список. То есть, такие независимые генераторы - это таски asyncio.

Кратко:
1. Когда ты эвейтишь специальный объект, ты делаешь yield и отдаешь управление циклу событий
2. Когда ты эвейтишь другую корутину, ты просто проваливаешся внутрь
3. Когда цикл событий получает управление, он может заняться другими делами
4. У цикла событий дел два: дергать разные таски и ждать если все просили подождать внешнего мира

Дополнительные материалы:
https://peps.python.org/pep-3156
https://peps.python.org/pep-0380
https://t.me/opensource_findings/869
https://www.gevent.org/intro.html
  • ❤ 95
  • 👍 43
  • 🔥 13
  • 🐳 6
  • 🤯 3
  • ✍ 2
  • 👨‍💻 2
  • 🤝 2
  • 💯 1
More from @advice17
  1. May 31, 2026Юзкейсы, сценарии и интеракторы Когда мы пишем наш код (программу, сервис, библиотеку), мы…
  2. Feb 17, 2026Меня позвали поговорить о разном: о проектах, опыте, программировании в целом. https://www…
  3. Jan 11, 2026SQLAlchemy и ORM SQLAlchemy в python предоставляет два набора API: 1. Возможность конструи…
  4. Nov 4, 2025Asyncio и колбэки В прошлом посте мы рассмотрели как asyncio взаимодействует с генераторам…
  5. Sep 27, 2025Стейт при работе с базами данных Подходы работы с БД можно разделить на две группы: • Орие…
  6. Apr 2, 2025Anti corruption layer Часто в наших приложениях мы обращаемся к каким-то внешним системам.…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →