8.11. Асинхронні ітератори

Асинхронний ітератор — це версія ітератора для asyncio. Огляд Python представив синхронний протокол ітератора — __iter__ та __next__ — та цикл for, що ним керує. Асинхронні ітератори додають await до кожного кроку, тому ітератор може чекати між значеннями, не блокуючи цикл подій.

8.11.1. Протокол

Клас є асинхронним ітератором, якщо він реалізує

  • __aiter__(self) — повертає об’єкт ітератора, зазвичай self.

  • async def __anext__(self) — повертає наступне значення або підіймає StopAsyncIteration, якщо значень більше немає.

Відповідна конструкція циклу — async for

import asyncio


class FrameCounter:
    """Yield N integers, one per frame period."""

    def __init__(self, n, period_ms):
        self._n = n
        self._period_ms = period_ms
        self._i = 0

    def __aiter__(self):
        return self

    async def __anext__(self):
        if self._i >= self._n:
            raise StopAsyncIteration
        await asyncio.sleep_ms(self._period_ms)
        self._i += 1
        return self._i

async def main():
    async for n in FrameCounter(5, 200):
        print(n)

Цикл async for очікує кожен виклик __anext__, тому корутина, що виконує цикл, поступається циклу подій між значеннями, як і будь-який інший await.

8.11.2. Коли це доречно

Щоразу, коли застосунок має джерело даних, що надходять з часом, а споживач повинен ітерувати їх ліниво. Датчик, що видає зразок за період, мережевий сокет, що доставляє повідомлення, генератор кадрів — скрізь, де підходить форма while True: x = await source.next(); process(x), async for x in source є чистішим записом.

Для джерел, що генерують до кінцевої умови, ітератор підіймає StopAsyncIteration і async for завершується нормально. Для нескінченних джерел — безперервного потоку датчика, довготривалої підписки — цикл виконується доти, доки корутина, в якій він живе, не буде скасована або її задача не буде знищена.