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 יוצא כרגיל. עבור מקורות שלעולם אינם מסתיימים – זרם חיישן רציף, מנוי ארוך-טווח – הלולאה רצה עד שהקורוטינה שבה היא חיה מבוטלת או שהמשימה שלה מפורקת.