C#8 – Novidades – Parte 2

Continuando com a série de posts sobre as novidades da versão 8 do C#, vamos para o segundo post da série, onde iremos conhecer uma feature chamada Async Streams. Quem ainda não leu o primeiro post, poderá fazê-lo aqui.

Async Streams foi o nome escolhido para essa feature que combina o uso de métodos assíncronos (async/await) com o retorno de múltiplos valores através do yield. Até então isso não era possível, pois mesmo utilizando um método assíncrono, este não conseguia ser assíncrono na busca dos dados, mas sim na execução (a nível de aplicação). Em outras palavras, quando o método era executado, os dados vinham todos de uma única vez, pois a combinação com o yield return era impossível.

Se tentássemos combiná-los, teríamos o erro abaixo:

Imagem 1 – Erro ao utilizar o yield em um método assíncrono.

O que mudou?

Para suportar essa nova feature, três interfaces foram adicionadas na versão 2.1 do .NET standard e implementadas na versão 3.0 do .Net Core.

namespace System.Collections.Generic
{
    public interface IAsyncEnumerable
    {
        IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default);
    }

    public interface IAsyncEnumerator : IAsyncDisposable
    {
        T Current { get; }

        ValueTask MoveNextAsync();
    }
}

namespace System
{
    public interface IAsyncDisposable
    {
        ValueTask DisposeAsync();
    }
}

Essas novas interfaces são as versões assíncronas das já existentes IEnumerable<T>, IEnumerator<T> e IDisposable, portanto, se comportam de forma similar a estas últimas.

Na prática, temos uma navegação assíncrona em coleções que antes eram somente síncronas e com isso liberando a sua aplicação para realizar outra tarefa no intervalo entre uma solicitação e um recebimento de cada item da coleção.

Imaginemos uma aplicação conectada a um dispositivo IoT por exemplo, sua aplicação irá receber esses dados e em seguida processa-los, mas esse processamento agora poderá ser feito no momento da chegada dos dados. O consumo destes dados agora não bloqueia mais a aplicação durante o tempo de espera tampouco devolve um conjunto de dados de uma única vez.

Convertendo para uma versão assíncrona

Para este exemplo, iremos utilizar dois métodos chamados GetHeartBeat e ShowHeartBeat. Ambos estão em suas versões síncronas para que possamos acompanhar a execução e em seguida faremos a conversão para as suas respectivas versões assíncronas.

 private static IEnumerable<int> GetHeartBeat()
        {
            Console.WriteLine($"Last check: {DateTime.Now}");
            yield return new Random().Next(40, 250);
        }


private static void ShowHeartBeat()
        {
            for (int i = 0; i < 5; i++)
            {

                 foreach (var item in GetHeartBeat())
                {
                    Console.WriteLine($"Current heart beat: {item}\n");
                }
            }
        }


 static void Main(string[] args)
        {
            ShowHeartBeat();
        }

P.S. para uma melhor compreensão desta feature, será necessário conhecer o funcionamento da palavra reservada yield do C# , por isso é importante a leitura deste post.

Ao executarmos os métodos acima teremos o seguinte retorno:

Imagem 2 – Execução síncrona

Como podemos verificar, o retorno do método ocorreu de forma síncrona, todos os valores chegaram ao mesmo tempo, exatamente no mesmo segundo.

Vamos iniciar a conversão pelo método GetHeartBeat.

O primeiro passo será uma alteração na assinatura do método, utilizando o já famoso par de palavras async/await, iremos torna-lo assíncrono. Nosso método deve ficar assim:

        private async static IEnumerable<int> GetHeartBeatAsync()
        {
            await Task.Delay(3000);
            Console.WriteLine($"Last check: {DateTime.Now}");
            yield return new Random().Next(40, 250);
        }

Note também que foi incluído um delay de 3 segundos, aqui entra uma parte muito importante, pois isso irá simular algum dispositivo enviando/produzindo dados de forma assíncrona.

Até aqui não temos nada de C# 8, tudo o que foi utilizado, faz parte das versões anteriores do C#, embora do jeito que está, este código não ira funcionar, ainda falta a cereja do bolo.

Agora vem a parte nova, iremos substituir a interface IEnumerable pela sua versão assíncrona, a interface IAsyncEnumerable.

P.S.: Para manter a convenção, foi acrescentado o sufixo “Async” ao final de ambos os métodos.

Feito isso, nossa mudança está completa neste método, o resultado é o código abaixo:

        private async static IAsyncEnumerable<int> GetHeartBeatAsync()
        {
            await Task.Delay(3000);
            Console.WriteLine($"Last check: {DateTime.Now}");
            yield return new Random().Next(40, 250);
        }

Para finalizar, vamos ajustar nosso método ShowHeartBeat, método que itera na coleção gerada pelo método GetHeartBeatAsync. Para que possamos iterar de forma assíncrona no método GetHeartBeatAsync, o C# 8 permite que coloquemos um await antes do foreach, isso faz com que seja possível navegar em coleções assíncronas.

O resultado da alteração será o seguinte:

private async static void ShowHeartBeatAsync()
        {
            for (int i = 0; i < 5; i++)
            {

                await foreach (var item in GetHeartBeatAsync())
                {
                    Console.WriteLine($"Current heart beat: {item}\n");
                }
            }
        }

Pronto, agora podemos conferir os retornos:

Imagem 3 – Execução assíncrona

Agora o retorno chega de forma assíncrona, o delay que simula um intervalo de “produção” de dados por um dispositivo externo, é agora obedecido e nós temos um retorno conforme o método GetHEartBeatAsync o produz.

Como já dito anteriormente, poderíamos aproveitar esse intervalo de 3 segundos para processar outra coisa e não ter a aplicação bloqueada pela espera da próxima iteração.

Com isso, conhecemos mais uma novidade do C# 8.

Até a próxima.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s