8.11. Iteratori asincroni

Un iteratore asincrono è la versione asyncio di un iteratore. La Panoramica di Python ha introdotto il protocollo sincrono degli iteratori – __iter__ e __next__ – e il ciclo for che lo guida. Gli iteratori asincroni aggiungono un await a ogni passo, così l’iteratore può attendere tra un valore e l’altro senza bloccare l’event loop.

8.11.1. Il protocollo

Una classe è un iteratore asincrono quando implementa

  • __aiter__(self) – restituisce l’oggetto iteratore, di solito self.

  • async def __anext__(self) – restituisce il valore successivo, oppure solleva StopAsyncIteration quando non ce ne sono altri.

Il costrutto di ciclo corrispondente è 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)

Il ciclo async for attende ogni chiamata a __anext__, così la coroutine che esegue il ciclo cede il controllo all’event loop tra un valore e l’altro, esattamente come qualsiasi altro await.

8.11.2. Quando ricorrervi

Ogni volta che l’applicazione ha una sorgente di dati che arrivano nel tempo e il consumatore deve iterarli in modo pigro. Un sensore che produce un campione per periodo, un socket di rete che consegna messaggi, un generatore di frame – ovunque calzi la forma while True: x = await source.next(); process(x), async for x in source è la scrittura più pulita.

Per le sorgenti che emettono fino a una condizione di fine finita, l’iteratore solleva StopAsyncIteration e async for termina normalmente. Per le sorgenti che non finiscono mai – un flusso continuo da un sensore, una sottoscrizione di lunga durata – il ciclo viene eseguito finché la coroutine in cui vive non viene annullata o il suo task non viene smantellato.