8.10. Awaitable třídy

8.10.1. Co je awaitable

Když korutina zapíše await x, jazyk se x zeptá, jak na něj čekat. Jakýkoli objekt, který umí odpovědět, je awaitable. Existují dva druhy:

  • Objekty korutin, které vracejí funkce async def. Volání send_request() pokaždé vytvoří jeden z nich – objekt korutiny, nikoli výsledek funkce. Teprve zápis await send_request() skutečně spustí tělo.

  • Objekty třídy, která definuje metodu __await__. async def je zkratkou pro první druh; __await__ je ručním způsobem, jak vytvořit druhý druh.

Aplikační kód používá ve výchozím nastavení první druh. Druhý druh existuje pro vzácný případ, kdy objekt samotný potřebuje být tím, na co volající awaituje, nikoli výsledkem volání metody na něm.

8.10.2. Obě formy vedle sebe

Stejná logika zapsaná jako korutina a jako awaitable třída:

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

Na obojí lze awaitovat stejným způsobem:

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

Forma async def je kratší, čte se jako běžný Python a nechává jazyk spravovat veškerou administrativu kolem pozastavování a obnovování. Forma třídy v tomto příkladu nezískává nic navíc.

8.10.3. Kdy si forma třídy zaslouží své místo

Když objekt musí být něčím, co aplikace předává dál – nikoli funkcí, kterou volající vyvolá, aby získal korutinu – je forma třídy jedinou možností:

  • Hodnota podobná future, kterou aplikace vytvoří, předá producentovi a později na ni awaituje, aby získala vyprodukovaný výsledek.

  • Vlastní primitivum postavené nad existujícím, kde je přirozeným API await my_thing spíše než await my_thing.wait(). Vestavěným příkladem je samotná třída asyncio.Task – zápis await task funguje, protože Task definuje __await__.

Pro cokoli, co odpovídá tvaru zavolej funkci, získej korutinu, počkej na ni, vítězí async def.

8.10.4. Podrobnosti protokolu

__await__ je běžná metoda (nikoli async def), která vrací iterátor. Když ji zapíšeme jako generátor – s alespoň jedním yield v těle – stane se jím automaticky. Každý yield pozastaví korutinu, která na objekt awaituje, a předá řízení zpět smyčce událostí; generátor pokračuje, až smyčka úlohu příště naplánuje. Holé return (nebo dosažení konce) čekání ukončí; cokoli return vyprodukuje, stane se hodnotou výrazu await.

V praxi vzácná třída, která toto dělá, předává yieldování existujícímu awaitable, namísto aby smyčku řídila sama:

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

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

yield from znovu využívá __await__ podkladové události pro skutečné pozastavení. Většina asyncio aplikací nikdy nepotřebuje psát ani jednu z forem.