8.11. Iteratory asynchroniczne

Iterator asynchroniczny to asyncio-wa wersja iteratora. W Przeglądzie Pythona przedstawiono synchroniczny protokół iteratora – __iter__ i __next__ – oraz pętlę for, która go napędza. Iteratory asynchroniczne dodają await do każdego kroku, dzięki czemu iterator może czekać pomiędzy wartościami bez blokowania pętli zdarzeń.

8.11.1. Protokół

Klasa jest iteratorem asynchronicznym, gdy implementuje

  • __aiter__(self) – zwraca obiekt iteratora, zazwyczaj self.

  • async def __anext__(self) – zwraca następną wartość lub zgłasza StopAsyncIteration, gdy nie ma już więcej wartości.

Odpowiadającą jej konstrukcją pętli jest 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)

Pętla async for czeka (await) na każde wywołanie __anext__, więc korutyna napędzająca pętlę oddaje sterowanie pętli zdarzeń pomiędzy wartościami, dokładnie tak jak przy każdym innym await.

8.11.2. Kiedy z tego korzystać

Zawsze, gdy aplikacja ma źródło danych napływających w czasie, a konsument powinien iterować po nich leniwie. Sensor produkujący próbkę na okres, gniazdo sieciowe dostarczające komunikaty, generator ramek – wszędzie tam, gdzie pasuje schemat while True: x = await source.next(); process(x), czystszym zapisem jest async for x in source.

Dla źródeł emitujących dane aż do skończonego warunku końcowego iterator zgłasza StopAsyncIteration, a async for kończy się normalnie. Dla źródeł, które nigdy się nie kończą – ciągłego strumienia z sensora, długotrwałej subskrypcji – pętla działa do momentu anulowania korutyny, w której się znajduje, lub zburzenia jej zadania.