8.10. Classes aguardáveis

8.10.1. O que é um aguardável

Quando uma coroutine escreve await x, a linguagem pergunta a x como esperar por ele. Qualquer objeto que saiba responder é aguardável. Existem dois tipos:

  • Os objetos coroutine que as funções async def devolvem. Chamar send_request() produz um destes cada vez – o objeto coroutine, não o resultado da função. Escrever await send_request() é o que realmente executa o corpo.

  • Objetos de uma classe que define um método __await__. async def é a forma abreviada para o primeiro tipo; __await__ é a forma manual de criar o segundo tipo.

O código da aplicação usa o primeiro tipo por defeito. O segundo tipo existe para o caso raro em que o próprio objeto precisa de ser aquilo que o chamador usa com await, não o resultado de chamar um método nele.

8.10.2. As duas formas lado a lado

A mesma lógica escrita como coroutine e como classe aguardável:

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

Ambas podem ser usadas com awaitda mesma forma:

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

A forma async def é mais curta, lê-se como Python normal, e deixa a linguagem gerir toda a contabilidade de suspensão e retoma. A forma de classe não acrescenta nada neste exemplo.

8.10.3. Quando a forma de classe justifica a sua existência

Quando o objeto tem de ser algo que a aplicação passa – não uma função que o chamador invoca para obter uma coroutine – a forma de classe é a única opção:

  • Um valor semelhante a um future que a aplicação cria, entrega a um produtor, e usa com awaitposteriormente para obter o resultado produzido.

  • Uma primitiva personalizada construída sobre uma existente onde a API natural é await my_thing em vez de await my_thing.wait(). A própria classe asyncio.Task é um exemplo incorporado – escrever await task funciona porque Task define __await__.

Para tudo o que corresponde ao padrão chamar uma função, obter uma coroutine, aguardá-la, async def é a melhor escolha.

8.10.4. Os detalhes do protocolo

__await__ é um método regular (não async def) que devolve um iterador. Escrevê-lo como gerador – incluindo pelo menos um yield no corpo – torna-o um automaticamente. Cada yield suspende a coroutine que usa awaitno objeto e devolve o controlo ao event loop; o gerador é retomado na próxima vez que o loop escalonar a tarefa. Um return simples (ou chegar ao fim) termina a espera; o que o return produz torna-se o valor da expressão await.

Na prática, a classe rara que faz isto delega a produção do yield a um aguardável existente em vez de conduzir o loop diretamente:

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

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

O yield from reutiliza o __await__ do evento subjacente para a suspensão real. A maioria das aplicações asyncio nunca precisa de escrever qualquer das formas.