Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

An asynchronous for loop (async for) is a control flow statement designed to iterate over asynchronous iterables. It allows the Python event loop to suspend the execution of the current coroutine while waiting for the next item to be yielded, thereby preventing blocking operations during iteration.

Syntax

async for target in async_iterable:
    # Execute loop body

The Asynchronous Iterator Protocol

The async for statement operates strictly on objects that implement the Asynchronous Iterator Protocol (introduced in PEP 492). This protocol requires two specific dunder (double underscore) methods:
  1. __aiter__(self): Must return an asynchronous iterator object.
  2. __anext__(self): Must return an awaitable (typically a coroutine) that resolves to the next value in the sequence. When the iteration is exhausted, this awaitable must raise a StopAsyncIteration exception.

Execution Mechanics

When the Python interpreter encounters an async for loop, it performs the following sequence of operations under the hood:
  1. It calls type(async_iterable).__aiter__(async_iterable) to obtain the asynchronous iterator.
  2. It calls type(async_iterator).__anext__(async_iterator) to get an awaitable.
  3. It implicitly awaits the awaitable returned by __anext__(). This is the point where context switching can occur, yielding control back to the event loop.
  4. Upon resolution of the awaitable, it assigns the resulting value to the target variable.
  5. It executes the loop body.
  6. It repeats steps 2-5 until the __anext__() awaitable raises StopAsyncIteration, which the loop catches silently to terminate execution.

Implementation Example: Custom Async Iterable

To visualize the mechanics without relying on external libraries, here is a custom class implementing the protocol:
import asyncio

class AsyncCounter:
    def __init__(self, limit):
        self.limit = limit
        self.current = 0

    def __aiter__(self):
        # Returns the asynchronous iterator (self)
        return self

    async def __anext__(self):
        # Returns an awaitable that yields the next value or raises StopAsyncIteration
        if self.current >= self.limit:
            raise StopAsyncIteration
        
        await asyncio.sleep(0.1)  # Non-blocking suspension point
        self.current += 1
        return self.current

async def main():
    # The async for loop consumes the AsyncCounter
    async for number in AsyncCounter(3):
        print(number)

asyncio.run(main())

Asynchronous Generators

Writing classes with __aiter__ and __anext__ is verbose. Python provides Asynchronous Generators as a syntactic shortcut. If an async def function contains a yield statement, it is classified as an asynchronous generator function. Calling this function returns an asynchronous generator object (the iterator), which natively implements the asynchronous iterator protocol and can be consumed directly by async for.
import asyncio

async def async_generator(limit):
    for i in range(limit):
        await asyncio.sleep(0.1)
        yield i + 1

async def main():
    async for number in async_generator(3):
        print(number)

asyncio.run(main())

Technical Constraints

  • Scope: async for can only be used inside an async def function (a coroutine) or within an asynchronous comprehension. Using it in a synchronous function will raise a SyntaxError.
  • Type Restriction: You cannot use async for on standard synchronous iterables (like list, tuple, or standard generators). The target object must implement __aiter__. Attempting to use async for on a standard list will raise a TypeError: 'async for' requires an object with __aiter__ method.
Master Python with Deep Grasping Methodology!Learn More