8.11. Async iterators

async iterator คือเวอร์ชัน asyncio ของ iterator Python Overview ได้แนะนำโปรโตคอล iterator แบบ synchronous -- __iter__ และ __next__ -- และลูป for ที่ขับเคลื่อนมัน Async iterators เพิ่ม await ให้กับแต่ละขั้นตอน ดังนั้น iterator สามารถ รอ ระหว่างค่าต่างๆ โดยไม่บล็อก event loop

8.11.1. โปรโตคอล

class เป็น async iterator เมื่อมันใช้งาน

  • __aiter__(self) -- คืนอ็อบเจกต์ iterator มักเป็น 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__ แต่ละครั้ง ดังนั้น coroutine ที่รันลูปจะยึดควบคุมให้กับ event loop ระหว่างค่าต่างๆ เหมือนกับ await อื่นๆ

8.11.2. เมื่อไรควรใช้

เมื่อแอปพลิเคชันมีแหล่งข้อมูลที่มาถึง ตามเวลา และผู้ใช้ควรวนซ้ำแบบ lazy เซนเซอร์ที่ผลิตตัวอย่างต่อช่วงเวลา socket เครือข่ายที่ส่งข้อความ frame generator -- ที่ใดก็ตามที่รูปแบบ while True: x = await source.next(); process(x) เหมาะ async for x in source เป็นวิธีเขียนที่สะอาดกว่า

สำหรับแหล่งข้อมูลที่ส่งออกจนถึงเงื่อนไขสิ้นสุดที่แน่นอน iterator จะยก StopAsyncIteration และ async for จะออกปกติ สำหรับแหล่งข้อมูลที่ไม่มีที่สิ้นสุด -- กระแส sensor ต่อเนื่อง การ subscribe แบบรันนาน -- ลูปจะทำงานจนกว่า coroutine ที่มันอยู่จะถูกยกเลิกหรือ task ของมันถูกทำลาย