Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

Python Asynchronous Generators

Learn asynchronous generator functions, yield, suspended execution, incremental production, explicit aclose(), and deterministic cleanup with contextlib.aclosing() when iteration ends early.

Levels: Intermediate ~25 min read

Intermediate

An asynchronous generator function combines async def with yield. Calling it produces an asynchronous generator object instead of running the function body to completion. That object is an asynchronous iterator, so it can produce values incrementally for an async for loop.

async def with yield creates an asynchronous generator

A function defined with async def becomes an asynchronous generator function when its body contains a yield expression. Calling the function creates an asynchronous generator object. The body starts running only when iteration asks it for a value.

Interactive Runnable example
Edit

Ready to run.

Yield suspends the generator and keeps its local state

When an asynchronous generator reaches yield it produces one value and suspends. Its local variables keep their current bindings, so the next iteration can continue from the same point instead of starting the function again.

Interactive Runnable example
Edit

Ready to run.

Calling the function is lazy

Calling an asynchronous generator function does not execute its body. This makes asynchronous generators useful for incremental production: the producer does work when the consumer requests values, not when the generator object is created.

Interactive Runnable example
Edit

Ready to run.

An empty return or the end of the body finishes the generator

An asynchronous generator can finish naturally by reaching the end of its body. It may also use an empty return statement to stop early. Completion ends asynchronous iteration with StopAsyncIteration. An asynchronous generator cannot return a value with return value.

Interactive Runnable example
Edit

Ready to run.

Asynchronous generators can be closed explicitly

When an asynchronous generator owns cleanup work, its finally block can release that resource when the generator is closed. Calling aclose() requests asynchronous generator shutdown; it returns an awaitable that must be awaited.

Interactive Runnable example
Edit

Ready to run.

yield from is not allowed in an asynchronous generator

Asynchronous generators use yield to produce values and async for to consume asynchronous iterables. The synchronous yield from delegation form is a syntax error inside an asynchronous generator function.

Interactive Runnable example
Edit

Ready to run.

Use async generators as producers, not as a replacement for every async operation

This lesson owns the language feature that creates asynchronous iterators from async def plus yield. The asynchronous iteration lesson owns the __aiter__() / __anext__() protocol and the aiter() / anext() built-ins. Coroutines own async def without yield and await. More advanced generator control and event-loop coordination are separate topics.

Use contextlib.aclosing() when iteration can end early

Calling await generator.aclose() explicitly is useful when you control the cleanup point. When a consumer may stop an async for loop early, contextlib.aclosing(generator) lets async with await aclose() automatically as the block exits. This makes the generator's finally code run deterministically in the same task and context as iteration, rather than relying on event-loop shutdown to finalize it later.

Interactive Runnable example
Edit

Ready to run.

The value that matches the break condition is still yielded and processed. Exiting the async with block then awaits the generator's aclose() method, which resumes it for cleanup and runs its finally block. This is different from merely leaving an async generator partially consumed.

Quick check

Quick check

What makes an async def function an asynchronous generator function?

async def produce():
    

Quick check

Which context manager makes the asynchronous generator run its cleanup before the code after the block prints events?

import asyncio
from contextlib import aclosing, asynccontextmanager, nullcontext

@asynccontextmanager
async def wrapper_only(stream, events):
    # This wrapper records its own exit but does not close stream.
    try:
        yield stream
    finally:
        events.append("wrapper exited")

async def records(events):
    try:
        yield "item"
        yield "later"
    finally:
        events.append("generator closed")

async def main():
    events = []
    generator = records(events)
    async with  as values:
        async for value in values:
            break
    print(events)

asyncio.run(main())
Was this page helpful?

Mark this learning resource complete to track your learning progress.