8.10. Clase care pot fi așteptate (awaitable)

8.10.1. Ce este un obiect care poate fi așteptat (awaitable)

Atunci când o corutină scrie await x, limbajul îi cere lui x să spună cum trebuie așteptat. Orice obiect care știe să răspundă este awaitable. Există două tipuri:

  • Obiectele corutină pe care le returnează funcțiile async def. Apelarea send_request() produce de fiecare dată unul dintre acestea – obiectul corutină, nu rezultatul funcției. Scrierea await send_request() este ceea ce execută efectiv corpul.

  • Obiectele unei clase care definește o metodă __await__. async def este forma prescurtată pentru primul tip; __await__ este modul manual de a crea cel de-al doilea tip.

Codul aplicației folosește implicit primul tip. Cel de-al doilea tip există pentru cazul rar în care obiectul însuși trebuie să fie lucrul pe care apelantul îl așteaptă cu await, nu rezultatul apelării unei metode asupra lui.

8.10.2. Cele două forme alăturate

Aceeași logică scrisă ca o corutină și ca o clasă 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

Ambele pot fi așteptate cu await în același mod:

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

Forma async def este mai scurtă, se citește ca Python obișnuit și lasă limbajul să gestioneze toată contabilitatea de suspendare și reluare. Forma de clasă nu obține nimic în plus în acest exemplu.

8.10.3. Când forma de clasă își merită locul

Atunci când obiectul trebuie să fie ceva ce aplicația vehiculează – nu o funcție pe care apelantul o invocă pentru a obține o corutină – forma de clasă este singura opțiune:

  • O valoare de tip future pe care aplicația o creează, o predă unui producător și o așteaptă cu await mai târziu pentru a prelua rezultatul produs.

  • O primitivă personalizată construită deasupra uneia existente, unde API-ul natural este await my_thing mai degrabă decât await my_thing.wait(). Clasa asyncio.Task însăși este un exemplu încorporat – scrierea await task funcționează deoarece Task definește __await__.

Pentru orice se potrivește formei apelezi o funcție, obții o corutină, o aștepți, async def câștigă.

8.10.4. Detaliile protocolului

__await__ este o metodă obișnuită (nu async def) care returnează un iterator. Scrierea ei ca generator – incluzând cel puțin un yield în corp – o transformă automat în unul. Fiecare yield suspendă corutina care așteaptă obiectul cu await și predă controlul înapoi buclei de evenimente; generatorul se reia data următoare când bucla programează sarcina. Un simplu return (sau ajungerea la sfârșit) încheie așteptarea; ceea ce produce return devine valoarea expresiei await.

În practică, rara clasă care face acest lucru predă cedarea (yield) către un obiect awaitable existent, în loc să conducă ea însăși bucla:

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

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

yield from reutilizează metoda __await__ a evenimentului subiacent pentru suspendarea efectivă. Majoritatea aplicațiilor asyncio nu au nevoie niciodată să scrie vreuna dintre forme.