Сообщить об ошибке.

Асинхронный генератор

Наличие выражения yield в функции или методе, определенных с помощью async def, дополнительно определяет функцию как функцию асинхронного генератора

Когда вызывается функция асинхронного генератора, она возвращает асинхронный итератор, известный как объект асинхронного генератора. Затем этот объект управляет выполнением функции генератора. Объект асинхронного генератора обычно используется в инструкции async for/in в функции сопрограммы аналогично тому, как объект генератора использовался бы в инструкции for/in .

Вызов одного из методов асинхронного генератора возвращает объект ожидания с помощью await, а выполнение начинается, когда этот объект ожидается. В это время выполнение переходит к первому выражению yield, где оно снова приостанавливается, возвращая значение expression_list ожидающей с помощью await сопрограмме. Как и в случае с обычным генератором, приостановка означает, что сохраняется все локальное состояние, включая текущие привязки локальных переменных, указатель на инструкцию, внутренний стек вычисления и состояние любой обработки исключений. Когда выполнение возобновляется ожиданием await следующего объекта, возвращаемого методами асинхронного генератора, функция может выполняться точно так же, как если бы выражение yield было просто еще одним внешним вызовом. Значение выражения yield после возобновления зависит от метода, который возобновил выполнение. Если используется agen.__anext__(), то результатом будет None. В противном случае, если используется agen.asend(), результатом будет значение, переданное этому методу.

Если случается, что асинхронный генератор завершает работу раньше break, то отменяются вызывающая задача или другие исключения, запускается код очистки асинхронного генератора и, возможно, вызывает исключения или обращается к контекстным переменным в неожиданном контексте - возможно, по истечении срока службы задач, это зависит от того, или во время завершения цикла событий, когда вызывается перехват сборки мусора асинхронным генератором. Чтобы предотвратить такое поведение, вызывающий должен явно закрыть асинхронный генератор, вызвав метод agen.aclose() для завершения работы генератора и, в конечном счете, отсоединить его от цикла событий.

В функции асинхронного генератора выражения yield разрешены в любом месте конструкции try/except . Однако, если асинхронный генератор не возобновляется до его завершения (из-за достижения нулевого количества ссылок или из-за сборки мусора), то выражение yield в конструкции try/except может привести к сбою в выполнении ожидающих выполнения предложений finally. В этом случае цикл событий или планировщик, запускающий асинхронный генератор, несет ответственность за вызов метода agen.aclose() итератора асинхронного генератора и запуск результирующего объекта сопрограммы, что позволяет выполнять любые ожидающие выполнения finally.

Чтобы позаботиться о завершении цикла событий, цикл событий должен определить функцию финализатора, которая принимает асинхронный генератор-итератор и предположительно вызывает agen.aclose() и выполняет сопрограмму. Этот финализатор может быть зарегистрирован путем вызова sys.set_asyncgen_hooks(). При первой итерации асинхронный генератор-итератор сохранит зарегистрированный финализатор, который будет вызван после завершения.

Выражение yield from <expr>, при использовании в функции асинхронного генератора - является синтаксической ошибкой.

Реализация/протокол асинхронного генератора.

Ниже описаны методы асинхронного генератора, которые используются для управления выполнением функции генератора.

Примечание:

  • awaitable - это объект, который можно использовать в выражении wait. Может быть сопрограммой или объектом с методом __await__().

agen.__anext__():

Метод agen.__anext__() возвращает объект awaitable, который запускает выполнение асинхронного генератора или возобновляет его с последнего выполненного выражения yield. При возобновлении работы асинхронный генератор будет продолжен до следующего выражения yield, при этом в возвращаемом состоянии ожидания текущее выражение yield всегда оценивается как None. Значением expression_list выражения yield является значение исключения StopIteration, вызванное завершением сопрограммы. Если асинхронный генератор завершает работу без получения какого либо значения, то awaitable вызывает исключение StopAsyncIteration, сигнализирующее о завершении асинхронной итерации.

Этот метод обычно вызывается неявно асинхронным циклом for.

Проще говоря agen.__anext__() выполняет одну итерацию асинхронного генератора, возвращает объект awaitable, который использует исключение StopItered для "выдачи" значений и исключение StopAsyncItered для оповещения об окончании итерации. Асинхронные генераторы определяют оба этих метода.

async def genfunc():
    yield 1
    yield 2

gen = genfunc()

assert gen.__aiter__() is gen

assert await gen.__anext__() == 1
assert await gen.__anext__() == 2

await gen.__anext__()  # Эта строка будет вызывать StopAsyncIteration.

agen.asend(value):

Метод agen.asend() возвращает объект awaitable, который при запуске возобновляет выполнение асинхронного генератора. Как и в случае метода send() для обычного генератора, он "отправляет" значение value в асинхронный генератор и аргумент value становится результатом текущего выражения yield.

  • Если асинхронный генератор завершит работу, не получив другого значения, объект awaitable, возвращаемый методом asend(), вернет значение исключения StopIteration или поднимет исключение StopAsyncIteration.
  • Когда метод asend() вызывается для запуска асинхронного генератора, то он должен вызываться с None в качестве аргумента, потому что нет выражения yield, которое могло бы получить значение.
async def gen():
    await asyncio.sleep(0.1)
    v = yield 42
    print(v)
    await asyncio.sleep(0.2)

g = gen()

await g.asend(None)
# Вернет 42 после сна в течение 0,1 секунды.

await g.asend('hello')
# Напечатает 'hello' и поднимет StopAsyncIteration
#  после сна в течение 0,2 секунд.

agen.athrow(value)
agen.athrow(type[, value[, traceback]])
:

Изменено в Python 3.12: Вторая сигнатура agen.athrow(type[, value[, traceback]]) устарела и может быть удалена в будущей версии Python.

Метод agen.athrow() возвращает объект awaitable, который вызывает исключение типа type в точке, где асинхронный генератор был приостановлен и возвращает исключение StopIteration.

  • Если асинхронный генератор завершает работу без получения другого значения, объект awaitable вызывает исключение StopAsyncItered.
  • Если асинхронный генератор не перехватывает переданное исключение или вызывает другое исключение, то при запуске awaitable, это исключение распространяется на объект вызвавший awaitable.
async def gen():
    try:
        await asyncio.sleep(0.1)
        yield 'hello'
    except ZeroDivisionError:
        await asyncio.sleep(0.2)
        yield 'world'

g = gen()
v = await g.asend(None)
print(v)                
# Напечатает 'hello' после сна 0.1 секунды.

v = await g.athrow(ZeroDivisionError)
print(v)               
 # Напечатает 'world' после сна 0.2 секунды.

agen.aclose():

Метод agen.aclose() возвращает значение awaitable, которое при запуске выдает исключение GeneratorExit в асинхронный генератор в точке, где он был приостановлен.

  • Если асинхронный генератор затем корректно завершает работу, уже закрыт или вызывает GeneratorExit не перехватывая исключение, то возвращенный awaitable вызовет исключение StopIteration. Любые дальнейшие значение awaitable последующих вызовов асинхронного генератора, вызовут исключение StopAsyncIteration.
  • Если асинхронный генератор выдает значение, то awaitable вызывает исключение RuntimeError.
  • Если асинхронный генератор вызывает любое другое исключение, оно передается вызывающей стороне awaitable.
  • Если асинхронный генератор уже вышел из-за исключения или обычного выхода, то дальнейшие вызовы aclose() вернут объект awaitable, который ничего не делает.

Метод agen.aclose() очень похож на то, что метод close() делает с обычными генераторами Python, за исключением того, что для выполнения aclose() требуется цикл обработки событий.