8.11. Bộ lặp bất đồng bộ

Một bộ lặp bất đồng bộ là phiên bản asyncio của một bộ lặp. Tổng quan Python đã giới thiệu giao thức bộ lặp đồng bộ -- __iter____next__ -- và vòng lặp for điều khiển nó. Bộ lặp bất đồng bộ thêm một await vào mỗi bước, nhờ đó bộ lặp có thể chờ giữa các giá trị mà không chặn vòng lặp sự kiện.

8.11.1. Giao thức

Một lớp là bộ lặp bất đồng bộ khi nó triển khai

  • __aiter__(self) -- trả về đối tượng bộ lặp, thường là self.

  • async def __anext__(self) -- trả về giá trị tiếp theo, hoặc ném StopAsyncIteration khi không còn giá trị nào.

Cấu trúc vòng lặp tương ứng là 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)

Vòng lặp async for await từng lời gọi đến __anext__, nhờ đó coroutine chạy vòng lặp nhường cho vòng lặp sự kiện giữa các giá trị giống như bất kỳ await nào khác.

8.11.2. Khi nào nên dùng

Bất cứ khi nào ứng dụng có nguồn dữ liệu đến theo thời gian và người tiêu thụ nên lặp theo cách lười biếng. Một cảm biến tạo ra mẫu theo chu kỳ, một socket mạng chuyển giao tin nhắn, một bộ tạo khung hình -- bất cứ nơi nào có hình thức while True: x = await source.next(); process(x) phù hợp, async for x in source là cách viết gọn hơn.

Đối với các nguồn phát ra cho đến điều kiện kết thúc hữu hạn, bộ lặp ném StopAsyncIterationasync for thoát bình thường. Đối với các nguồn không bao giờ kết thúc -- luồng cảm biến liên tục, đăng ký chạy lâu dài -- vòng lặp chạy cho đến khi coroutine nó sống bị hủy hoặc tác vụ của nó bị phá hủy.