8.11. Async-iterators¶
Een async-iterator is de asyncio-versie van een iterator. Het Python-overzicht introduceerde het synchrone iteratorprotocol – __iter__ en __next__ – en de for-lus die het aandrijft. Async-iterators voegen aan elke stap een await toe, zodat de iterator kan wachten tussen waarden door zonder de event loop te blokkeren.
8.11.1. Het protocol¶
Een klasse is een async-iterator wanneer deze het volgende implementeert
__aiter__(self)– geeft het iteratorobject terug, doorgaansself.async def __anext__(self)– geeft de volgende waarde terug, of werptStopAsyncIterationop wanneer er geen meer zijn.
De bijbehorende lusconstructie is 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)
De async for-lus wacht (await) op elke aanroep van __anext__, zodat de coroutine die de lus uitvoert tussen waarden door de controle teruggeeft aan de event loop, net als bij elke andere await.
8.11.2. Wanneer je het inzet¶
Telkens wanneer de applicatie een gegevensbron heeft die in de loop van de tijd binnenkomt en de consument deze lui (lazy) moet doorlopen. Een sensor die per periode een meetwaarde produceert, een netwerksocket die berichten aflevert, een framegenerator – overal waar de vorm while True: x = await source.next(); process(x) past, is async for x in source de nettere schrijfwijze.
Voor bronnen die uitzenden tot een eindige eindvoorwaarde werpt de iterator StopAsyncIteration op en eindigt async for normaal. Voor bronnen die nooit eindigen – een continue sensorstroom, een langlopend abonnement – loopt de lus totdat de coroutine waarin deze leeft wordt geannuleerd of de bijbehorende taak wordt afgebroken.