3.1 Módulo asyncio
Já faz tempo que ninguém cria suas próprias threads para tarefas assíncronas. Quer dizer, dá pra criar, mas essas ações são consideradas muito de baixo nível e só são usadas por desenvolvedores de frameworks. E isso quando realmente não tem outro jeito.
Agora tá na moda
programação assíncrona, operadores async/await
e corrotinas com tasks. Mas vamos por partes...
Um pouco de história
Inicialmente, no Python, para resolver problemas de programação assíncrona
eram usadas corrotinas, baseadas em geradores.
Depois, no Python 3.4, surgiu o módulo
asyncio
(às vezes chamado de async IO
), que implementa mecanismos de programação assíncrona.
No Python 3.5, apareceu a construção async/await
.
Agora um pouco sobre o contexto. Primeiro eu vou falar brevemente sobre todas essas coisas, depois com mais detalhes, e depois ainda mais detalhes. Não dá pra fazer diferente, porque quase todas elas funcionam juntas, e explicar em detalhe o funcionamento de uma sem fazer referência às outras não vai rolar.
Módulo asyncio
O módulo asyncio
é destinado à escrita de programas assíncronos, proporcionando
a possibilidade de executar tarefas
em paralelo. Ele suporta operações assíncronas de entrada-saída,
timers, sockets, execução de corrotinas e multithreading, funcionando em
um ou mais loops de eventos.
Corrotinas (Coroutines)
Corrotinas são
funções assíncronas definidas com a palavra-chave
async def
. Corrotinas permitem
suspender sua execução com a palavra-chave await
, o que
permite a execução de outras corrotinas nesse meio tempo
.
import asyncio
# Definição de uma função assíncrona (corrotina)
async def main():
print('Hello ...')
# Suspendemos a execução por 1 segundo
await asyncio.sleep(1)
print('... World!')
# Execução da função assíncrona main() no loop de eventos
asyncio.run(main())
Loop de Eventos (Event Loop)
O loop de eventos gerencia a execução de corrotinas, tasks e outras
operações assíncronas. Chamar asyncio.run()
inicia o loop de eventos e
executa a corrotina até a conclusão.
import asyncio
async def main():
print('Hello ...')
await asyncio.sleep(1)
print('... World!')
# Obtenção do loop de eventos atual
loop = asyncio.get_event_loop()
# Execução da corrotina até a conclusão
loop.run_until_complete(main())
# Fechamento do loop de eventos após a conclusão de todas as tarefas
loop.close()
Tasks (Tasks)
Tasks permitem executar corrotinas em paralelo. Criadas com
asyncio.create_task()
ou
asyncio.ensure_future()
.
import asyncio
# Definição de uma corrotina que será executada com atraso
async def say_after(delay, what):
# Suspendemos a execução pelo tempo especificado
await asyncio.sleep(delay)
print(what)
# Corrotina principal
async def main():
# Criamos tarefas para execução paralela de corrotinas
task1 = asyncio.create_task(say_after(1, 'hello'))
task2 = asyncio.create_task(say_after(2, 'world'))
# Aguardamos a conclusão de ambas as tarefas
await task1
await task2
# Execução da corrotina principal
asyncio.run(main())
Futures (Futures)
Objetos Future
representam os resultados de operações assíncronas,
que estarão disponíveis no futuro. Por exemplo, são usados para
aguardar a conclusão de uma tarefa assíncrona.
import asyncio
# Definição de uma corrotina que simula uma tarefa longa
async def long_running_task():
print('Task started')
# Suspendemos a execução por 3 segundos
await asyncio.sleep(3)
print('Task finished')
return 'Result'
# Corrotina principal
async def main():
# Criamos um future para esperar a conclusão da tarefa
future = asyncio.ensure_future(long_running_task())
# Aguardamos a conclusão da tarefa e obtemos o resultado
result = await future
print(f'Task result: {result}')
# Execução da corrotina principal
asyncio.run(main())
3.2 Função Assíncrona — async def
Uma função assíncrona é declarada do mesmo jeito que uma função comum, mas antes
da palavra-chave def
você deve escrever a palavra
async
.
async def NomeDaFuncao(parametros):
código da função
Uma função assíncrona é declarada como uma normal, chamada como uma normal, mas o resultado que ela retorna é diferente. Se você chamar uma função assíncrona, ela vai retornar não o resultado, mas um objeto especial — corrotina.
Dá até pra testar isso:
import asyncio
async def main():
print("Hello World")
# Chamada de uma função assíncrona, que retorna uma corrotina
result = main()
# Verificamos o tipo do resultado
print(type(result)) # <class 'coroutine'>
O que acontece? Quando você marca uma função com o async, você na verdade adiciona um decorador a ela, que faz algo mais ou menos assim:
def async_decorator(func):
# Criamos um objeto Task
task = Task()
# Passamos nossa função func para ele executar
task.target = func
# Adicionamos o objeto task na fila de tarefas — Event Loop
eventloop.add_task(task)
# Retornamos o objeto task
return task
E seu código fica parecido com:
import asyncio
@async_decorator
def main():
print("Hello World")
result = main()
print(type(result)) # <class 'coroutine'>
O sentido dessa analogia é o seguinte:
Quando você chama a função assíncrona, cria-se um objeto especial Task
, que vai executar sua
função, mas em algum momento no futuro. Pode ser em 0.0001 seg, ou pode ser em 10.
Esse objeto task
é imediatamente retornado como resultado da chamada da sua função assíncrona. Isso é
uma corrotina.
Sua função assíncrona pode nem ter começado a ser executada ainda, mas o objeto task
(corrotina) você já tem.
Pra que serve esse task
(corrotina)? Você não pode fazer muita coisa com ele, mas pode fazer 3 coisas:
- Esperar até que a função assíncrona termine de executar.
- Esperar até que a função assíncrona termine de executar e obter o resultado da execução da corrotina.
- Esperar até que 10 (ou qualquer número) de funções assíncronas terminem de executar.
Como fazer isso, eu vou te contar abaixo.
3.3 Operador await
A maior parte das ações com corrotinas começa com "esperar pela execução da função assíncrona". Por isso, para essa
ação temos um operador especial await
.
Você só precisa escrevê-lo antes da corrotina:
await corrotina
Ou direto antes da chamada da função assíncrona:
await funcao_assincrona(argumentos)
Quando o Python encontra no código o operador await
, ele suspende a execução da função atual
e espera até que a
corrotina termine de executar — até que a função assíncrona referenciada pela corrotina não seja concluída.
Importante!
O operador await
é usado somente dentro de uma função assíncrona para suspender a execução até
que outra corrotina ou operação assíncrona termine.
Isso é feito para simplificar o processo de alternar entre as chamadas de funções assíncronas. Essa chamada
await
é na verdade uma declaração "vamos esperar aqui por um tempo desconhecido — tratem de executar outras funções
assíncronas".
Exemplo:
import asyncio
# Definição de uma função assíncrona
async def async_print(text):
print(text)
# Função assíncrona principal
async def main():
# Usamos await para aguardar a execução da função assíncrona
await async_print("Hello World")
# Inicia o loop de eventos principal e executa a corrotina main()
asyncio.run(main()) #executa a função assíncrona
Na verdade, o operador await
funciona de forma ainda mais inteligente — ele também retorna o resultado
da execução
da função assíncrona onde foi chamado.
Exemplo:
import asyncio
# Definição de uma função assíncrona que soma dois números
async def async_add(a, b):
return a + b
# Função assíncrona principal
async def main():
# Usamos await para obter o resultado da execução de async_add
sum = await async_add(100, 200)
print(sum)
# Inicia o loop de eventos principal e executa a corrotina main()
asyncio.run(main()) #executa a função assíncrona
Então, resumindo, o operador await
:
- Suspende a função assíncrona atual até que outra corrotina ou operação assíncrona termine.
- Retorna o resultado da operação assíncrona ou corrotina.
- Pode ser usado somente dentro de uma função assíncrona.
GO TO FULL VERSION