ZetCode

Python anext Function

Last modified April 11, 2025

This comprehensive guide explores Python's anext function, which retrieves the next item from an asynchronous iterator. We'll cover async iterators, error handling, and practical examples of asynchronous iteration.

Basic Definitions

The anext function is the async equivalent of next. It retrieves the next item from an asynchronous iterator or returns a default value if provided and iterator is exhausted.

Key characteristics: works with async iterators, must be awaited, raises StopAsyncIteration when exhausted (unless default is provided), and was introduced in Python 3.10.

Basic Async Iterator Usage

Here's simple usage with an async iterator showing how anext retrieves values from an asynchronous source.

basic_anext.py
import asyncio

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

    def __aiter__(self):
        return self

    async def __anext__(self):
        if self.current >= self.stop:
            raise StopAsyncIteration
        await asyncio.sleep(0.1)
        self.current += 1
        return self.current

async def main():
    counter = AsyncCounter(3)
    print(await anext(counter))  # 1
    print(await anext(counter))  # 2
    print(await anext(counter))  # 3

asyncio.run(main())

This example shows anext with a custom async iterator. Each call to anext must be awaited and returns the next value from the iterator.

The AsyncCounter class implements the async iterator protocol with __aiter__ and __anext__ methods. Note the StopAsyncIteration exception for signaling completion.

Using Default Value

anext can accept a default value to return instead of raising StopAsyncIteration when the iterator is exhausted.

default_value.py
import asyncio

async def async_gen():
    yield "first"
    yield "second"

async def main():
    ag = async_gen()
    print(await anext(ag))            # 'first'
    print(await anext(ag))            # 'second'
    print(await anext(ag, "default")) # 'default'

asyncio.run(main())

This example demonstrates the default value behavior. After yielding two values, the async generator is exhausted. The third anext call returns the default instead of raising an exception.

This pattern is useful when you want to handle the end of iteration without try/except blocks. The default can be any value, including None.

Error Handling

This example shows proper error handling when using anext with async iterators.

error_handling.py
import asyncio

async def failing_async_gen():
    yield 1
    raise ValueError("Something went wrong")

async def main():
    ag = failing_async_gen()
    try:
        print(await anext(ag))  # 1
        print(await anext(ag))  # Raises ValueError
    except ValueError as e:
        print(f"Caught error: {e}")

    # Exhausted iterator case
    ag = failing_async_gen()
    try:
        await anext(ag)
        await anext(ag)
        await anext(ag)  # Would raise StopAsyncIteration
    except StopAsyncIteration:
        print("Iterator exhausted")

asyncio.run(main())

These examples demonstrate error handling with anext. The first case shows handling a custom exception from the iterator. The second shows catching StopAsyncIteration.

Proper error handling is crucial when working with async iterators as they can raise both iteration-related and domain-specific exceptions.

With Async Generators

anext works seamlessly with async generators, which are the most common way to create async iterators.

async_generators.py
import asyncio

async def async_data_fetcher():
    for i in range(3):
        await asyncio.sleep(0.1)
        yield f"data-{i}"

async def main():
    fetcher = async_data_fetcher()
    while True:
        try:
            data = await anext(fetcher)
            print(f"Received: {data}")
        except StopAsyncIteration:
            print("No more data")
            break

asyncio.run(main())

This example shows anext with an async generator function. The generator yields values asynchronously, and anext retrieves them one by one.

The while-try-except pattern is common when consuming async iterators where you don't know the exact number of items in advance.

Real-world API Pagination

This example demonstrates a practical use case: paginating through an async API using anext.

api_pagination.py
import asyncio
from typing import AsyncIterator, Dict, Any

class AsyncAPIClient:
    def __init__(self):
        self.page = 0
    
    async def fetch_page(self) -> Dict[str, Any]:
        self.page += 1
        if self.page > 3:
            return {"items": [], "has_more": False}
        
        await asyncio.sleep(0.2)
        return {
            "items": [f"item-{self.page}-{i}" for i in range(2)],
            "has_more": self.page < 3
        }

    def __aiter__(self) -> AsyncIterator[str]:
        return self
    
    async def __anext__(self) -> str:
        if not hasattr(self, "_current_page"):
            self._current_page = await self.fetch_page()
            self._items_iter = iter(self._current_page["items"])
        
        try:
            return next(self._items_iter)
        except StopIteration:
            if not self._current_page["has_more"]:
                raise StopAsyncIteration
            
            self._current_page = await self.fetch_page()
            self._items_iter = iter(self._current_page["items"])
            return await self.__anext__()

async def main():
    client = AsyncAPIClient()
    try:
        while True:
            item = await anext(client)
            print(f"Processing: {item}")
    except StopAsyncIteration:
        print("All items processed")

asyncio.run(main())

This example implements pagination through an async API. The AsyncAPIClient fetches pages of data and yields individual items. anext handles the iteration through both page items and page transitions.

The implementation shows how to manage complex async iteration state while providing a simple interface to consumers via anext.

Best Practices

Source References

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.