2.32. Corrotinas¶
Uma corrotina é uma função que pode pausar no meio do caminho e mais tarde retomar de onde parou, com todas as suas variáveis locais intactas. Ela espelha o padrão dos geradores – um corpo de função que pode ser suspenso e retomado – com uma mudança em quem comanda cada retomada.
A sintaxe do Python para escrever uma corrotina é o par de palavras-chave async / await. async marca a função como uma corrotina; await marca os pontos dentro dela onde é permitido pausar.
2.32.1. Definindo uma corrotina¶
Uma função definida com async def é uma função corrotina. Chamá-la não executa o corpo; ela retorna um objeto corrotina que ainda não começou:
async def greet(name):
print("hello,", name)
coro = greet("Alice") # body NOT run yet
O objeto corrotina está pausado bem no início da função. Algo precisa comandá-lo para fazer o corpo rodar – esse algo é um laço de eventos (event loop), um componente de tempo de execução. O MicroPython traz um no módulo asyncio. Por enquanto, trate a corrotina como “pronta para rodar, esperando por um comandante”.
2.32.2. Pausando dentro de uma corrotina¶
Dentro de uma corrotina, uma expressão await suspende a execução até que o valor aguardado esteja pronto:
async def fetch_and_log():
data = await read_sensor()
print("got:", data)
Quando o corpo chega a await read_sensor(), a corrotina devolve o controle a quem quer que a esteja executando. Quando read_sensor() termina, o comandante retoma a corrotina na linha seguinte, com o resultado vinculado a data.
await só é válido dentro de uma corrotina. Usá-lo em uma função comum é um erro de sintaxe.
2.32.3. Relação com os geradores¶
Corrotinas e geradores compartilham o mesmo mecanismo subjacente. A diferença está em quem aciona cada retomada:
Um gerador produz valores; o consumidor puxa o próximo com
next()ou iterando.Uma corrotina cede controle; um laço de eventos agenda a retomada quando a operação aguardada está pronta.
Se o aperto de mão do yield dos geradores faz sentido, o aperto de mão das corrotinas é a mesma ideia – apenas comandado por um laço de eventos em vez de um laço for.
Um laço de eventos (event loop) é um pequeno despachante que mantém uma lista de corrotinas esperando por algo (um timer, um evento de rede, outra corrotina terminando). A cada iteração ele escolhe uma corrotina cuja espera foi satisfeita, a retoma até o próximo await, então registra pelo que aquela corrotina está esperando agora e passa para outra que esteja pronta. O resultado são muitas tarefas progredindo concorrentemente em uma única thread – cada corrotina cede o controle voluntariamente em seus pontos await, e o laço preenche esses momentos com quaisquer outras corrotinas que estejam prontas para progredir.
Por baixo dos panos, await e yield usam o mesmo recurso de tempo de execução do Python para suspender e retomar uma função. As palavras-chave diferem porque a convenção em torno delas difere: yield devolve um valor a um consumidor que o puxa com next(); await entrega o controle a um laço de eventos que agenda a retomada quando a operação aguardada está pronta. async / await é essencialmente uma sintaxe mais nova para o padrão de corrotina – bibliotecas mais antigas construíam corrotinas diretamente sobre o maquinário de geradores, usando yield from (apresentado em Iteradores e geradores) para delegar a suspensão entre corrotinas.
2.32.4. Corrotinas precisam de um comandante¶
Uma corrotina é inerte sem um tempo de execução para comandá-la. Defini-la é tranquilo; executá-la requer um laço de eventos. O módulo asyncio do MicroPython fornece esse laço de eventos. A seção Asyncio cobre como iniciar o laço, agendar corrotinas nele, compartilhar estado entre elas com locks e eventos, lidar com cancelamento e timeouts, e moldar uma aplicação real em torno das palavras-chave async / await apresentadas aqui.