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 ממתינה (await) לכל קריאה ל-__anext__, כך שהקורוטינה שמריצה את הלולאה מוסרת שליטה ללולאת האירועים בין ערכים בדיוק כמו כל await אחר.
8.11.2. מתי להשתמש בזה#
בכל פעם שלאפליקציה יש מקור נתונים שמגיע לאורך זמן והצרכן צריך לעבור עליו באופן עצל. חיישן שמייצר דגימה אחת לכל פרק זמן, שקע רשת שמספק הודעות, מחולל פריימים – בכל מקום שבו הצורה while True: x = await source.next(); process(x) מתאימה, async for x in source היא הכתיבה הנקייה יותר.
עבור מקורות שפולטים נתונים עד לתנאי סיום סופי, האיטרטור מעלה StopAsyncIteration וה-async for יוצא כרגיל. עבור מקורות שלעולם אינם מסתיימים – זרם חיישן רציף, מנוי ארוך-טווח – הלולאה רצה עד שהקורוטינה שבה היא חיה מבוטלת או שהמשימה שלה מפורקת.