O que é Programação Assíncrona e como utilizá-la?
A Programação assíncrona é uma forma de evitar delays ou tempos de espera na execução de um programa. Quando estamos executando algo sincronicamente, podemos ter bloqueios no processo pela necessidade de esperar alguma execução de código. Isso pode bloquear o programa como um todo até que termine a execução deste passo.
Async e Await são as palavras chaves usadas no C# para usar a programação assíncrona. Nesse conteúdo vamos explicar o que é programação assíncrona e como utilizá-la.
Quando usar a programação assíncrona?
Podemos usar programação assíncrona sempre que tivermos um procedimento que possa ser independente, tais como:
- Leitura e escrita de um arquivo;
- Chamadas de recursos 3rd party;
- Lógicas independentes que podem ser separadas da execução da thread principal.
Tipos de retornos
Void: quando utilizamos este retorno em um método async, estamos assumindo que ele irá ser executado em uma thread paralelizada, mas não poderá ser awaitable. Ou seja, não poderemos usar o atributo await nele para esperar o seu resultado.
Este conceito é chamado de "Fire and forget".
Task: corresponde a um retorno do tipo void, mas que é awaitable. Ou seja, podemos usar o await para esperar a sua execução. Já em um método void, nãohaverá retorno de tipo algum.
Task T: este retorno é também awaitable, mas nele teremos um generic que indica o tipo do retorno que estamos esperando, sendo T, qualquer tipo que desejarmos.
Na prática
O SO gerencia a thread do sistema sendo uma única thread que executa passo a passo de forma procedural, ou seja de maneira síncrona.
Quando trabalhamos com formato assíncrono, podemos ter várias execuções de processos (threads) sem bloquearmos a thread principal ou as demais se assim desejarmos. Dessa forma, podemos trabalhar de forma paralelizada.
Observe uma chamada síncrona:
Chamada Assíncrona:
Algumas vezes, necessitamos o resultado de uma chamada feita de modo assíncrono para o prosseguimento de nossa operação. Nesses casos, podemos usar o operador await.
O operador Await se faz necessário quando precisamos de um resultado em meio a um processo para continuar, fazendo com que nosso procedimento aguarde o retorno do que estamos chamando. Isso tudo sem bloquear a thread principal, o que significa que a aplicação não fica travada.
É importante lembrar que, para o desenvolvedor, o uso do async await pode parecer muito com o uso do formato síncrono.Porém, por baixo dos panos, não é bem assim que funciona.
Abaixo, apresento exemplos de como usar o async await de forma síncrona e paralelizada.
Apenas para ilustrar, temos os métodos de Get que buscam os dados na api pública da JsonPlaceHolder, que nos retorna coleções de objetos Json para simularmos uma massa de dados obtida ( https://jsonplaceholder.typicode.com ):
Neste endpoint temos a execução síncrona dos métodos. Mesmo eles sendo Async, chamamos eles com o .Result() para que sejam executados de maneira síncrona.
Neste segundo exemplo, já de forma assíncrona, executamos os métodos, porém aguardamos sua execução com o await.
Em teoria, isso funcionaria de forma semelhante a execução síncrona, com a diferença de que para cada execução temos uma thread nova, mesmo que as outras aguardem o seu término:
Neste terceiro endpoint temos uma otimização do conceito assíncrono. Comparando com o anterior, podemos ver que, no início do método, disparamos as chamadas dos métodos Get e atribuímos às variáveis.
Neste cenário, threads diferentes são disparadas para cada chamada, e isso é possível porque ainda não precisamos dos seus valores. Mas quando vamos utilizar os seus valores para algo, necessitamos que eles estejam prontos.
Aí sim utilizamos o await, para que se a thread de chamada do método Get ainda não tiver terminado a sua execução o sistema espere ela, ou elas…
Dessa forma, concluímos que a utilização do async await vai além de apenas utilizá-lo no código, onde métodos que contém o async/await não necessariamente estão sendo executados de forma paralelizada.
Para obtermos tal resultado, devemos pensar melhor na estrutura de sua utilização e como queremos que os processos se comportem. De um modo geral, processos async/await bem estruturados nos retornam ganho de tempo por podermos executarmos “n” processos de forma assíncrona.
Gostou de saber um pouco mais sobre Programação Assíncrona e suas principais implicações?
Confira mais conteúdos como esse em nosso blog!
Quer ser nosso próximo Tech Writer? Confira nossas vagas na página Carreira!
Até a próxima!