8.10. Các lớp awaitable

8.10.1. Awaitable là gì

Khi một coroutine viết await x, ngôn ngữ hỏi x cách chờ nó. Bất kỳ đối tượng nào biết cách trả lời đều là awaitable. Có hai loại:

  • Các đối tượng coroutine mà hàm async def trả về. Gọi send_request() tạo ra một cái mỗi lần -- đối tượng coroutine, không phải kết quả của hàm. Viết await send_request() mới thực sự chạy phần thân.

  • Các đối tượng của lớp định nghĩa phương thức __await__. async def là cách viết tắt cho loại đầu tiên; __await__ là cách thủ công để tạo loại thứ hai.

Mã ứng dụng sử dụng loại đầu tiên theo mặc định. Loại thứ hai tồn tại cho trường hợp hiếm khi chính đối tượng cần là thứ người gọi await, không phải kết quả của việc gọi một phương thức trên nó.

8.10.2. Hai dạng cạnh nhau

Cùng một logic viết dưới dạng coroutine và dưới dạng lớp awaitable:

import asyncio

# async def -- almost always the right choice
async def yield_then_return(value):
    await asyncio.sleep_ms(0)
    return value

# awaitable class -- equivalent, rarely written
class YieldThenReturn:
    def __init__(self, value):
        self._value = value

    def __await__(self):
        yield                       # let the loop run once
        return self._value          # value of the ``await`` expression

Cả hai đều có thể được awaited theo cùng cách:

async def main():
    a = await yield_then_return(1)
    b = await YieldThenReturn(2)
    print(a, b)                     # prints: 1 2

Dạng async def ngắn hơn, đọc như Python thông thường, và để ngôn ngữ quản lý tất cả việc theo dõi tạm dừng-và-tiếp tục. Dạng lớp không có thêm gì trong ví dụ này.

8.10.3. Khi dạng lớp xứng đáng vị trí của nó

Khi đối tượng phải thứ ứng dụng truyền xung quanh -- không phải một hàm mà người gọi gọi để lấy coroutine -- dạng lớp là lựa chọn duy nhất:

  • Một giá trị giống future mà ứng dụng tạo ra, truyền cho producer, và awaits sau để lấy kết quả được tạo ra.

  • Một nguyên thủy tùy chỉnh xây dựng trên một cái đã có sẵn, nơi API tự nhiên là await my_thing thay vì await my_thing.wait(). Lớp asyncio.Task chính nó là một ví dụ có sẵn -- viết await task hoạt động vì Task định nghĩa __await__.

Đối với bất cứ thứ gì có dạng gọi hàm, lấy coroutine, await nó, async def thắng.

8.10.4. Chi tiết giao thức

__await__ là phương thức thông thường (không phải async def) trả về một bộ lặp. Viết nó dưới dạng generator -- bao gồm ít nhất một yield trong thân -- tự động làm nó thành một. Mỗi yield tạm dừng coroutine đang awaiting đối tượng và trả quyền điều khiển cho vòng lặp sự kiện; generator tiếp tục vào lần tiếp theo vòng lặp lập lịch tác vụ. Một return đơn thuần (hoặc kết thúc hàm) kết thúc quá trình chờ; bất cứ thứ gì return tạo ra trở thành giá trị của biểu thức await.

Trong thực tế, lớp hiếm nào làm điều này sẽ chuyển giao việc yield cho một awaitable đã có sẵn thay vì tự điều khiển vòng lặp:

class WaitForEvent:
    def __init__(self, event):
        self._event = event

    def __await__(self):
        yield from self._event.wait().__await__()
        return self._event

yield from tái sử dụng __await__ của sự kiện cơ bản cho việc tạm dừng thực sự. Hầu hết các ứng dụng asyncio không bao giờ cần viết cả hai dạng.