8.11. 非同步迭代器

非同步迭代器(async iterator)是迭代器的 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. 何時該使用它

只要應用程式有一個隨時間陸續到達的資料來源,而消費端應該以惰性方式迭代它時,就適合使用。每隔一段時間產生一筆樣本的感測器、傳遞訊息的網路 socket、影格產生器 -- 任何符合 while True: x = await source.next(); process(x) 形式的地方,async for x in source 都是更俐落的寫法。

對於會持續發出值直到某個有限結束條件的來源,迭代器會引發 StopAsyncIteration,而 async for 會正常結束。對於永不結束的來源 -- 持續不斷的感測器串流、長時間執行的訂閱 -- 迴圈會一直執行,直到它所在的協程被取消或其任務被拆除為止。