Trilha de aprendizado · Nível 13 · Tutorial 6

Criar corrotinas com async e await

Ao concluir, você será capaz de escrever e executar corrotinas simples, distinguindo espera cooperativa de chamadas que bloqueiam o laço de eventos.

  • Nível: Intermediário
  • Duração: 15 min
  • 7 passos
Criar corrotinas com async e await

O que você vai percorrer

  1. Distinguir a função do objeto corrotina Veja a diferença entre definir uma corrotina, chamá-la e obter futuramente seu resultado. 2 min
  2. Aguardar e usar o resultado com await Use await para executar uma corrotina chamada, receber seu valor de retorno e utilizá-lo em outra corrotina. 2 min
  3. Executar a entrada assíncrona com asyncio.run Use asyncio.run para iniciar uma corrotina principal a partir de um arquivo Python executado de forma síncrona. 2 min
  4. Entender a espera cooperativa Veja quando uma corrotina realmente libera o laço de eventos e por que usar await não cria concorrência por conta própria. 2 min
  5. Prever a sequência de awaits consecutivos Entenda por que dois awaits escritos em sequência executam uma operação de cada vez. 2 min
  6. Identificar o que bloqueia o laço Compare espera cooperativa e bloqueio para reconhecer operações que impedem o progresso do laço de eventos. 2 min
  7. Aplicar e revisar em um script completo Construa e execute um script local que reúne definição de corrotinas, await sequencial, asyncio.run e espera cooperativa. 3 min

O que você vai aprender

  • Definir uma corrotina com async def e consumir seu resultado com await.
  • Executar uma corrotina principal a partir de um programa síncrono com asyncio.run.
  • Explicar por que chamadas consecutivas com await não criam concorrência por si só.
  • Identificar chamadas bloqueantes e trechos de processamento que impedem o progresso de outras tarefas.

Antes de começar

  • Escolher entre threads, processos e asyncio
  • Definir funções com parâmetros e retorno
  • Tratar exceções com try, except, else e finally

Passo 1 de 7

Distinguir a função do objeto corrotina

Veja a diferença entre definir uma corrotina, chamá-la e obter futuramente seu resultado.

Uma definição com async def

Uma função de corrotina

Assim como def, async def define uma função que pode receber parâmetros e ter return. A diferença é que ela define uma função de corrotina: seu corpo não começa a rodar apenas porque a função foi chamada.

Definição

python
async def buscar_nome(codigo: int) -> str:
    print("Consultando dados...")
    return f"cliente-{codigo}"

Definir não é executar

Diagrama mostrando async def criando uma função de corrotina, com o corpo contendo print e return ainda inativo.

async def cria a função. As instruções do corpo ainda não foram executadas.

Chamar cria um objeto corrotina

A chamada ainda não produz o nome

Observe a chamada abaixo. Ela cria um objeto corrotina e o guarda em pendente. Nesse momento, nem o print nem o return do corpo aconteceram. Portanto, pendente não contém a string retornada pela função.

Criação do objeto

python
pendente = buscar_nome(7)

print(pendente)
# Exibe uma representação de um objeto corrotina.
# "Consultando dados..." ainda não foi exibido.

Dica

Três elementos diferentes

buscar_nome é a função de corrotina; buscar_nome(7) produz o objeto corrotina; a string "cliente-7" seria o valor retornado quando o corpo fosse executado e concluído. A forma de executar e obter esse valor virá no próximo passo.

Associe cada expressão ao que ela representa

Função, objeto ou resultado?

Relacione cada elemento à sua descrição.

Toque em um item e depois no par correspondente.

Passo 2 de 7

Aguardar e usar o resultado com await

Use await para executar uma corrotina chamada, receber seu valor de retorno e utilizá-lo em outra corrotina.

De objeto corrotina a resultado

O que await recebe

Uma chamada como buscar_nome(7) cria um objeto corrotina. Esse objeto é aguardável: ele pode ser usado com await.

Dentro de uma função criada com async def, escreva await buscar_nome(7) para executar essa corrotina e obter o valor de seu return. Assim, o valor recebido pode ser atribuído a uma variável e usado normalmente.

Fluxo de um await

Diagrama mostrando a corrotina principal chamando uma corrotina interna, aguardando-a e recebendo um valor de volta.

A chamada produz o objeto corrotina; await obtém o valor retornado pelo corpo dela.

Compor uma corrotina com outra

Uma corrotina usa o retorno da outra

Por enquanto, observe apenas a composição. No próximo passo, você verá como iniciar principal a partir de um script síncrono.

python
async def buscar_nome(codigo):
    return f"usuário-{codigo}"


async def principal():
    nome = await buscar_nome(7)
    mensagem = f"Cadastro encontrado: {nome}"
    return mensagem

Leia o resultado

Em principal, a expressão await buscar_nome(7) resulta em "usuário-7". Portanto, nome recebe essa string e mensagem passa a ser "Cadastro encontrado: usuário-7".

await não serve para valores comuns. Por exemplo, await 7 e await "olá" geram erro, porque um inteiro e uma string não são objetos aguardáveis.

Dica

Regra prática

Use await antes de uma expressão que produz um objeto aguardável, como a chamada de uma função definida com async def. No contexto destes scripts, escreva isso dentro de outra função com async def.

Complete a composição

Qual palavra falta?

Complete a linha dentro de async def principal():

codigo = ____ buscar_codigo()

Identifique o valor recebido

Considere:

async def buscar_nome():
    return "Lia"

async def principal():
    nome = await buscar_nome()

Qual valor é atribuído a nome?

Passo 3 de 7

Executar a entrada assíncrona com asyncio.run

Use asyncio.run para iniciar uma corrotina principal a partir de um arquivo Python executado de forma síncrona.

A fronteira entre código síncrono e assíncrono

O papel de asyncio.run

Em um script comum, o ponto de entrada é síncrono: o Python começa a executar o arquivo de cima para baixo. Para iniciar uma corrotina nesse contexto, importe asyncio e use asyncio.run(...).

asyncio.run recebe o objeto corrotina criado pela chamada de uma função async, executa essa corrotina em um laço de eventos que ele gerencia e encerra esse laço quando a corrotina termina. Quando asyncio.run retorna, o código síncrono pode continuar.

Fluxo de execução

A chamada da corrotina cria o objeto que será entregue a asyncio.run.

Diagrama mostrando código síncrono chamando asyncio.run com um objeto corrotina, a corrotina principal sendo executada no laço de eventos e o fluxo retornando ao código síncrono.

O script entra na parte assíncrona por asyncio.run(...) e volta quando a corrotina principal acaba.

Script completo para executar

Crie um arquivo .py

No seu computador, crie um arquivo chamado entrada_async.py e copie o código abaixo. Usar um arquivo .py evita depender de ambientes interativos que podem já estar executando um laço de eventos.

entrada_async.py

Um programa autocontido com uma entrada assíncrona.

python
import asyncio


async def buscar_mensagem(nome: str) -> str:
    print("Dentro da corrotina")
    return f"Olá, {nome}!"


async def principal() -> None:
    mensagem = await buscar_mensagem("Ana")
    print(mensagem)


if __name__ == "__main__":
    print("Antes de asyncio.run")
    asyncio.run(principal())
    print("Depois de asyncio.run")

Exemplo

Execute e confira

No terminal, na pasta do arquivo, execute:

python entrada_async.py

A ordem esperada é:

Antes de asyncio.run
Dentro da corrotina
Olá, Ana!
Depois de asyncio.run

A expressão principal() não executa imediatamente o corpo de principal: ela produz o objeto corrotina passado para asyncio.run.

Uma condição importante

Atenção

Não chame asyncio.run dentro de um laço já ativo

asyncio.run deve ser usado quando ainda não há um laço de eventos em execução na mesma thread. Chamá-lo de dentro de uma corrotina ou de outro contexto que já tenha um laço ativo gera erro.

Para esta prática, execute o arquivo .py pelo terminal, como no exemplo. Não é necessário criar ou configurar um laço manualmente.

Dica

Use uma entrada principal clara

A proteção if __name__ == "__main__": mantém a entrada do programa explícita: ela chama asyncio.run(principal()) somente quando você executa o arquivo diretamente.

Observe a fronteira de execução

Relate o que você observou

Depois de executar o arquivo, descreva a ordem das mensagens exibidas. Em sua explicação, diga qual é o papel de asyncio.run(principal()) entre os dois print síncronos.

Escreva pelo menos 80 caracteres (0/80).

Passo 4 de 7

Entender a espera cooperativa

Veja quando uma corrotina realmente libera o laço de eventos e por que usar await não cria concorrência por conta própria.

Suspender abre uma oportunidade

Espera cooperativa

O asyncio usa cooperação: a corrotina em execução precisa chegar a uma oportunidade de suspensão para que o laço de eventos possa dar andamento a outro trabalho que já esteja agendado e pronto.

await é uma possível oportunidade, mas não uma promessa de troca de execução. Se o aguardável já produz seu resultado imediatamente, o await pode terminar sem que a corrotina ceda o controle.

Além disso, uma suspensão não cria trabalho novo nem significa execução paralela: ela apenas deixa o laço disponível durante a espera.

Duas consequências possíveis de await

Compare um resultado imediato com uma espera cooperativa.

Diagrama em duas faixas: na primeira, uma corrotina usa await em uma operação que retorna imediatamente e continua sem intervalo; na segunda, uma corrotina usa await em uma espera temporizada, fica suspensa e o laço de eventos permanece disponível para trabalho já existente.

Um await pode concluir imediatamente ou suspender a corrotina. Apenas no segundo caso o laço fica disponível enquanto ela espera.

Resultado imediato e espera temporizada

O que observar

Neste exemplo, resposta_imediata() retorna assim que é executada. Portanto, aguardá-la não fornece, por si só, um momento útil de suspensão.

Já await asyncio.sleep(1) simula uma espera de um segundo sem bloquear a thread. Durante esse intervalo, o laço poderia prosseguir com outro trabalho que já tivesse sido agendado. Como este script não agenda outro trabalho, ele apenas retoma pausar() após a espera.

Script autocontido

Salve como espera_cooperativa.py e execute com python espera_cooperativa.py.

python
import asyncio


async def resposta_imediata() -> str:
    print("A corrotina imediata começou")
    return "pronto"


async def pausar() -> None:
    print("Antes da espera cooperativa")
    await asyncio.sleep(1)
    print("Depois da espera cooperativa")


async def principal() -> None:
    valor = await resposta_imediata()
    print(f"Resultado: {valor}")
    await pausar()


asyncio.run(principal())

Dica

Uso apropriado

asyncio.sleep() é útil para simular uma espera controlada neste tipo de exemplo. Ele não transforma processamento pesado em cooperativo nem cria concorrência sozinho.

Verifique sua interpretação

Todo await suspende?

Toda expressão com await obrigatoriamente suspende a corrotina atual e faz outra tarefa executar.

Passo 5 de 7

Prever a sequência de awaits consecutivos

Entenda por que dois awaits escritos em sequência executam uma operação de cada vez.

Um await por vez

Sequência não é concorrência

Quando uma corrotina usa await diretamente em duas chamadas consecutivas, ela só alcança a segunda depois que a primeira termina e devolve seu resultado. Durante a espera cooperativa da primeira, o laço fica livre para trabalho que já exista, mas esse código ainda não iniciou a segunda operação.

Linha do tempo da sequência

A operação B começa somente após o fim de A.

Diagrama de linha do tempo em que a operação A inicia, espera cooperativamente e termina; só então a operação B inicia, espera e termina. O laço de eventos aparece disponível durante as esperas.

Liberar o laço não cria automaticamente uma segunda operação.

Observe as mensagens

Duas operações aguardadas em sequência

Salve e execute este exemplo em um arquivo Python, se quiser observar a ordem das mensagens.

python
import asyncio


async def operar(nome: str) -> str:
    print(f"início: {nome}")
    await asyncio.sleep(1)
    print(f"fim: {nome}")
    return f"resultado de {nome}"


async def principal() -> None:
    primeiro = await operar("A")
    segundo = await operar("B")
    print(primeiro)
    print(segundo)


asyncio.run(principal())

Exemplo

Ordem esperada

A ordem das mensagens é:

  1. início: A
  2. fim: A
  3. início: B
  4. fim: B

As duas esperas simuladas ocorrem uma após a outra. Assim, a duração total tende a ser próxima da soma das esperas, além do pequeno custo do restante do programa. Use a ordem das mensagens como evidência; não dependa de tempos exatos.

Criar antes não inicia antes

Objetos corrotina ainda não são trabalho em andamento

Também não há concorrência se você apenas criar os dois objetos antes dos awaits. A chamada cria os objetos, mas seus corpos continuam sem execução até serem aguardados neste exemplo.

Criação antecipada, espera sequencial

python
async def principal() -> None:
    corrotina_a = operar("A")
    corrotina_b = operar("B")

    primeiro = await corrotina_a
    segundo = await corrotina_b

    print(primeiro, segundo)

Dica

Regra prática

Criar um objeto corrotina não é iniciar a operação. E um await direto interrompe o fluxo desta corrotina até que aquele aguardável termine. Formas explícitas de agendar trabalho serão vistas no próximo tutorial.

Verifique sua previsão

Ordene as mensagens

No primeiro código, coloque as mensagens na ordem em que aparecem.

  1. início: B
  2. fim: A
  3. fim: B
  4. início: A

Explique a ausência de concorrência

Por que operar("B") não começa enquanto operar("A") está em await asyncio.sleep(1) no primeiro código?

Escreva pelo menos 40 caracteres (0/40).

Passo 6 de 7

Identificar o que bloqueia o laço

Compare espera cooperativa e bloqueio para reconhecer operações que impedem o progresso do laço de eventos.

O que deixa o laço indisponível

`async def` não torna o corpo mágico

Uma corrotina ainda executa na thread do laço de eventos. Se ela chama uma operação síncrona demorada ou passa muito tempo calculando sem se suspender, essa thread fica ocupada. Enquanto isso, o laço não consegue dar andamento a outro trabalho já agendado.

Laço disponível × laço bloqueado

Compare os dois tipos de espera.

Diagrama lado a lado: à esquerda, uma corrotina suspensa durante asyncio.sleep e o laço livre; à direita, time.sleep ou processamento intenso ocupa a thread e o laço fica parado.

await asyncio.sleep(...) libera o laço durante a espera; uma chamada bloqueante mantém a thread ocupada.

Atenção

Atenção à aparência do código

Colocar uma chamada bloqueante dentro de async def não a transforma em não bloqueante. E fazer await de uma corrotina que contém essa chamada apenas espera sua conclusão: não libera o laço durante o bloqueio.

Duas esperas, efeitos opostos

Contraste entre espera bloqueante e cooperativa

Ambas parecem “esperar”, mas afetam o laço de modos diferentes.

python
import asyncio
import time

async def espera_bloqueante():
    print("início bloqueante")
    time.sleep(2)  # bloqueia a thread do laço
    print("fim bloqueante")

async def espera_cooperativa():
    print("início cooperativo")
    await asyncio.sleep(2)  # suspende esta corrotina
    print("fim cooperativo")

Como interpretar

time.sleep(2) mantém a thread parada por 2 segundos. Já await asyncio.sleep(2) suspende a corrotina; nesse intervalo, o laço fica disponível para trabalho que já esteja agendado. Isso não cria trabalho concorrente sozinho nem faz duas chamadas consecutivas deixarem de ser sequenciais.

Dica

Correção com escopo limitado

Troque time.sleep por await asyncio.sleep quando a espera for artificial, como uma simulação de atraso. Essa troca não resolve uma biblioteca síncrona demorada, leitura/gravação síncrona ou processamento intenso.

Outras fontes de bloqueio

Processamento longo também ocupa a thread

O problema não é apenas usar sleep.

python
async def calcular_total(valores):
    total = 0
    for valor in valores:
        total += valor * valor  # muito trabalho aqui pode ocupar o laço
    return total

async def carregar_dados():
    with open("dados.txt", encoding="utf-8") as arquivo:
        return arquivo.read()  # entrada síncrona pode demorar

Sinais para investigar

Entrada e saída síncronas demoradas — como acesso a arquivos, rede ou banco de dados por APIs síncronas — podem bloquear. O mesmo vale para cálculos extensos em Python sem oportunidades de suspensão. O próximo tutorial tratará de tarefas; mais adiante, você verá formas apropriadas de integrar trabalho bloqueante.

Verifique a correção apropriada

Escolha a melhor alteração

Uma corrotina usa time.sleep(1) apenas para simular a demora de uma operação. Qual alteração é apropriada?

Passo 7 de 7

Aplicar e revisar em um script completo

Construa e execute um script local que reúne definição de corrotinas, await sequencial, asyncio.run e espera cooperativa.

Script completo: duas operações em sequência

Monte um arquivo local

Crie um arquivo chamado operacoes.py no seu computador. O exemplo usa apenas a biblioteca padrão: cada chamada a consultar simula uma espera cooperativa, retorna um texto e só então a próxima chamada começa.

operacoes.py

python
import asyncio


async def consultar(nome: str) -> str:
    print(f"Início: {nome}")
    await asyncio.sleep(1)
    print(f"Fim: {nome}")
    return f"resultado de {nome}"


async def principal() -> None:
    primeiro = await consultar("A")
    segundo = await consultar("B")

    print(primeiro)
    print(segundo)


asyncio.run(principal())

Fluxo da execução

Diagrama mostrando o código síncrono chamando asyncio.run, que executa principal; principal aguarda consultar A terminar antes de chamar e aguardar consultar B, e então retorna ao código síncrono.

Os dois awaits estão em sequência: B só inicia depois que A termina.

Execute e observe a ordem

Rode no terminal

No terminal, na pasta onde você salvou o arquivo, execute:

python operacoes.py

A ordem esperada das mensagens é: início de A, fim de A, início de B, fim de B. Depois aparecem os dois valores retornados. Os tempos exatos não são importantes; observe a ordem.

Relate sua execução

Quais mensagens apareceram e em qual ordem? Inclua os dois valores retornados e explique, em uma frase, por que B não começou antes do fim de A.

Escreva pelo menos 80 caracteres (0/80).

Corrija uma espera que bloqueia

Async não torna uma chamada automaticamente cooperativa

Se a espera simulada fosse escrita com time.sleep(1), a thread do laço ficaria bloqueada durante esse segundo. Para uma espera artificial dentro de uma corrotina, use await asyncio.sleep(1).

Essa troca libera o laço enquanto a corrotina está suspensa, mas não faz A e B virarem operações concorrentes: os awaits em principal continuam consecutivos.

Troca pontual

Evite a versão bloqueante abaixo e mantenha a versão cooperativa do script.

python
# Bloqueia a thread do laço durante a espera:
# import time
# time.sleep(1)

# Suspende cooperativamente esta corrotina:
await asyncio.sleep(1)

Explique a correção

Por que trocar time.sleep(1) por await asyncio.sleep(1) é importante dentro dessa corrotina? O que permanece igual na ordem entre A e B?

Escreva pelo menos 100 caracteres (0/100).

Síntese e próximo passo

Resumo

O que você consolidou

  • async def define a corrotina; chamá-la produz um objeto que será aguardado.
  • await obtém o retorno da corrotina dentro de outra corrotina.
  • asyncio.run(principal()) é a entrada síncrona do script e gerencia o laço para essa execução.
  • Dois awaits diretos, um após o outro, mantêm as operações sequenciais.
  • await asyncio.sleep(...) é uma espera cooperativa; chamadas síncronas demoradas e processamento longo bloqueiam o laço.
  • Para iniciar trabalho concorrente de modo explícito, serão apresentadas tarefas no próximo tutorial.

Tutorial concluído

Parabéns! Você concluiu: Criar corrotinas com async e await

Muito bem! Você consegue definir corrotinas, aguardar seus resultados, iniciá-las com asyncio.run e reconhecer quando uma espera bloqueia o laço de eventos.

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso