Python async / await explicado: o tutorial 2026
Async em Python tem a reputação de ser confuso. Não precisa ser. Depois de internalizar uma frase - async permite que UM thread execute várias tarefas vinculadas a E/S intercaladas - todo o resto segue.
Este tutorial cobre: o que async def e await realmente fazem, quando o async vence versus quando é a ferramenta errada, os quatro padrões que toda base de código assíncrona usa (sequencial, coleta, semáforo, tempo limite) e as três maneiras pelas quais os iniciantes engolem erros silenciosamente.
O modelo mental de uma frase
Async em Python é simultaneidade cooperativa em um único thread. Quando seu código atinge await, o controle retorna ao loop de eventos, que pode executar outra tarefa enquanto a primeira aguarda E/S. Quando a E/S for concluída, o loop retoma a primeira tarefa de onde foi pausado.
É isso. Sem threads, sem processos, sem paralelismo (no sentido CPython-com-GIL). Apenas um thread fazendo malabarismos com muitas tarefas que passam a maior parte do tempo aguardando na rede/disco/banco de dados.
Async é mais rápido quando o gargalo está esperando, não computando.
O programa assíncrono mínimo viável de três linhas
async def hello()— declara uma co-rotina. Chamarhello()não executa o código; ele cria um objeto de co-rotina.await asyncio.sleep(1)— devolve o controle ao loop de eventos por 1 segundo.asyncio.run(hello())— cria um loop de eventos e executa a corrotina até a conclusão.
Regra: `await` somente dentro de `async def`. Chamar await em uma função regular é um SyntaxError.
Quando o assíncrono vence (e quando não)
Vitórias assíncronas: trabalho vinculado a E/S
Qualquer tarefa que gaste a maior parte do tempo ESPERANDO é candidata a assíncrona:
- Solicitações HTTP (APIs externas, scraping, webhooks)
- Consultas de banco de dados
- Leituras/gravações de arquivos
- Fluxos WebSocket/gRPC
- Consumidores de fila (Redis, RabbitMQ, Kafka)
Exemplo: buscando 100 URLs.
Aceleração do relógio de parede de 50 a 100x para trabalhos pesados de E/S. É aqui que o assíncrono ganha seu orçamento de complexidade.
Async PERDAS: trabalho vinculado à CPU
Async não oferece paralelismo para computação. Por ser de thread único cooperativo, um loop ativo da CPU bloqueia todo o loop de eventos.
Todas as outras tarefas morrem de fome enquanto pbkdf2 é executado. Para trabalho vinculado à CPU, use multiprocessing ou concurrent.futures.ProcessPoolExecutor, não assíncrono.
Carga de trabalho mista: run_in_executor
Se UMA etapa for pesada para a CPU e o restante for E/S, envolva a etapa da CPU para que ela seja executada em um pool de threads sem bloquear o loop:
O None = pool de threads padrão. Ótimo para chamar bibliotecas de sincronização de terceiros que são lentas, mas não podem ser reescritas.
Os quatro padrões
1. Sequencial – uma tarefa por vez
Use quando cada solicitação depender do resultado da anterior.
2. asyncio.gather — dispara tudo de uma vez
Escolha padrão para tarefas de E/S independentes. Mas: se você reunir 10.000 tarefas em uma API, obterá descritores de arquivo com taxa limitada ou de exaustão. É aí que entra o padrão do semáforo.
3. Semáforo - simultaneidade máxima
asyncio.Semaphore(N) permite no máximo N tarefas passarem do async with sem: a qualquer momento. As outras tarefas ficam na fila. Use para qualquer API externa com limite de taxa ou banco de dados com pool de conexões.
4. Tempo limite – abandone o trabalho lento
asyncio.wait_for cancela a tarefa subjacente após N segundos. Essencial para qualquer código assíncrono que se comunica com uma rede — nunca faça uma espera ilimitada.
As três armadilhas para engolir erros
Armadilha 1: asyncio.gather com return_exceptions=True
Útil quando você deseja manter resultados parciais, mas LETAL se você não verificar todos os resultados:
Sem a verificação, os erros desaparecem silenciosamente.
Armadilha 2: tarefas do tipo disparar e esquecer
Se background_work() aumentar, o Python emite um aviso no desligamento e segue em frente. Para qualquer coisa que lhe interesse, armazene a tarefa e await ou use asyncio.TaskGroup (3.11+) que propaga todos os erros.
Armadilha 3: Sincronize o código dentro do assíncrono
requests é síncrono. Mesmo dentro de um async def, requests.get bloqueia todo o loop até que a resposta chegue. Use httpx (ou aiohttp) para HTTP assíncrono.
Regra: nunca use uma biblioteca de E/S síncrona dentro de código assíncrono. Se a biblioteca não tiver uma variante assíncrona, envolva-a em run_in_executor.
asyncio.TaskGroup — a maneira de 2026 de se espalhar
Python 3.11 adicionou TaskGroup, que corrige o problema de disparar e esquecer de forma limpa:
Simultaneidade estruturada — sem erros perdidos, sem tarefas órfãs. Prefira isso ao invés de create_task no código moderno.
Exemplo do mundo real: scraper com taxa limitada
Todos os quatro padrões em 15 linhas: simultaneidade limitada (semáforo), tempos limite (wait_for), ramificações de erro explícitas (sem engolir) e coleta para distribuição. Copie e cole esta forma em qualquer tarefa de E/S assíncrona.
Async em Python tem uma curva de aprendizado real, mas o modelo mental é simples: um thread, pausas cooperativas em await, sobreposições de E/S. Domine gather + Semaphore + wait_for + TaskGroup e você terá coberto 95% do trabalho assíncrono que irá escrever.
Próxima etapa: o curso Python sênior tem 20 lições práticas sobre asyncio, TaskGroup, aiohttp/httpx e os padrões de simultaneidade acima — com Python real sendo executado em seu navegador.