8.11. Iteradores asíncronos¶
Un iterador asíncrono es la versión asyncio de un iterador. La Introducción a Python presentó el protocolo de iteradores síncrono – __iter__ y __next__ – y el bucle for que lo impulsa. Los iteradores asíncronos añaden un await a cada paso, de modo que el iterador puede esperar entre valores sin bloquear el bucle de eventos.
8.11.1. El protocolo¶
Una clase es un iterador asíncrono cuando implementa
__aiter__(self)– devuelve el objeto iterador, normalmenteself.async def __anext__(self)– devuelve el siguiente valor, o lanzaStopAsyncIterationcuando ya no hay más.
La construcción de bucle correspondiente es 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)
El bucle async for espera (await) cada llamada a __anext__, de modo que la corrutina que ejecuta el bucle cede el control al bucle de eventos entre valores, igual que cualquier otro await.
8.11.2. Cuándo recurrir a ellos¶
Siempre que la aplicación tenga una fuente de datos que llegan a lo largo del tiempo y el consumidor deba iterarla de forma perezosa. Un sensor que produce una muestra por periodo, un socket de red que entrega mensajes, un generador de fotogramas – en cualquier lugar donde encaje la forma while True: x = await source.next(); process(x), async for x in source es la forma más limpia de escribirlo.
Para fuentes que emiten hasta una condición de fin finita, el iterador lanza StopAsyncIteration y async for finaliza con normalidad. Para fuentes que nunca terminan – un flujo continuo de sensor, una suscripción de larga duración – el bucle se ejecuta hasta que la corrutina en la que vive se cancela o su tarea se desmonta.