8.10. Awaitable classes

8.10.1. awaitable คืออะไร

เมื่อ coroutine เขียน await x ภาษาจะถาม x ว่าจะรอมันอย่างไร อ็อบเจกต์ใดก็ตามที่รู้วิธีตอบคือ awaitable มีสองประเภท:

  • อ็อบเจกต์ coroutine ที่ฟังก์ชัน async def คืนค่า การเรียก send_request() จะสร้างอันหนึ่งทุกครั้ง -- coroutine อ็อบเจกต์ ไม่ใช่ผลลัพธ์ของฟังก์ชัน การเขียน await send_request() คือสิ่งที่รันเนื้อหาจริงๆ

  • อ็อบเจกต์ของ class ที่นิยาม method __await__ async def คือชวเลขสำหรับประเภทแรก __await__ คือวิธีด้วยตนเองในการสร้างประเภทที่สอง

โค้ดแอปพลิเคชันใช้ประเภทแรกโดยค่าเริ่มต้น ประเภทที่สองมีอยู่สำหรับกรณีที่หายากที่ อ็อบเจกต์เอง จำเป็นต้องเป็นสิ่งที่ผู้เรียก await, ไม่ใช่ผลลัพธ์ของการเรียก method บนมัน

8.10.2. สองรูปแบบเปรียบเทียบกัน

ตรรกะเดียวกันเขียนเป็น coroutine และเป็น awaitable class:

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

ทั้งสองสามารถ awaitได้ในรูปแบบเดียวกัน:

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

รูปแบบ async def สั้นกว่า อ่านเหมือน Python ปกติ และให้ภาษาจัดการการบุ๊กคีปปิ้งสำหรับการ suspend-and-resume ทั้งหมด รูปแบบ class ไม่ได้รับอะไรพิเศษในตัวอย่างนี้

8.10.3. เมื่อรูปแบบ class มีคุณค่า

เมื่ออ็อบเจกต์ต้อง เป็น บางอย่างที่แอปพลิเคชันส่งต่อกัน -- ไม่ใช่ฟังก์ชันที่ผู้เรียกเรียกเพื่อรับ coroutine -- รูปแบบ class คือตัวเลือกเดียว:

  • ค่า future-like ที่แอปพลิเคชันสร้าง ส่งให้ producer และ awaitในภายหลังเพื่อดึงผลลัพธ์ที่ผลิตแล้ว

  • primitive ที่กำหนดเองที่สร้างบน primitive ที่มีอยู่ซึ่ง API ตามธรรมชาติคือ await my_thing แทนที่จะเป็น await my_thing.wait() class asyncio.Task เองเป็นตัวอย่างในตัว -- การเขียน await task ทำงานได้เพราะ Task นิยาม __await__

สำหรับอะไรก็ตามที่เข้ากับรูปแบบ เรียกฟังก์ชัน รับ coroutine await มัน, async def ชนะ

8.10.4. รายละเอียดโปรโตคอล

__await__ เป็น method ปกติ (ไม่ใช่ async def) ที่คืน iterator การเขียนเป็น generator -- รวมถึง yield อย่างน้อยหนึ่งตัวในเนื้อหา -- ทำให้เป็นอันโดยอัตโนมัติ แต่ละ yield จะ suspend coroutine ที่ awaitอ็อบเจกต์และส่งการควบคุมกลับไปยัง event loop generator จะ resume ครั้งถัดไปที่ลูปกำหนดเวลา task return แบบเปล่า (หรือสิ้นสุดโดยไม่มี return) จบการรอ ค่าที่ return ผลิตจะกลายเป็นค่าของ expression await

ในทางปฏิบัติ class ที่หายากที่ทำสิ่งนี้จะส่ง yielding ไปยัง awaitable ที่มีอยู่แทนที่จะขับเคลื่อน loop เอง:

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

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

yield from ใช้ __await__ ของ event ที่มีอยู่สำหรับการ suspending จริง แอปพลิเคชัน asyncio ส่วนใหญ่ไม่ต้องการเขียนรูปแบบใดทั้งสอง