C# - Fluxo assíncrono ou async streams


 Hoje veremos o novo recurso do C# 8.0 chamado Asycn Streams que permite tratar um fluxo assíncrono.

Um dos muitos recursos do C# 8 é o streams asynchronous ou fluxo assíncrono., que são uma nova forma de iterar sobre um fluxo de dado. Antes podíamos fazer isso de forma síncrona.

Antes do C# 8, você podia usar a palavra-chave await apenas para obter um único resultado. A partir do C# 8.0, usando await agora podemos obter um fluxo de resultados, e, isso foi possível através da definição de interfaces iteradoras assíncronas e atualizações no loop foreach e na declaração de yield.

Para usar este recurso você precisa estar usando o .NET Core 3.0 SDK ou superior e o compilador C# 8.0. Isso estará configurado por padrão se você estiver usando o Visual Studio 2019 versão 16.3 ou superior.

Um método que retorna um fluxo assíncrono possui três propriedades,

  1. Deve ter uma declaração com o modificador async;
  2. O tipo de retorno deve ser IAsyncEnumerable<T>;
  3. O corpo do método precisa conter pelo menos um return yield para retornar elementos sucessivos no fluxo assíncrono;

Consumir um fluxo assíncrono requer que você adicione a palavra-chave await antes da palavra-chave foreach ao enumerar os elementos do fluxo.

A adição da palavra-chave await requer que o método que enumera o fluxo assíncrono seja declarado com o modificador async e retorne um tipo permitido para um método assíncrono.  Normalmente, isso significa retornar uma Task ou Task<TResult>. Também pode ser um ValueTask ou ValueTask<TResult>.

Um método pode também consumir e produzir um fluxo assíncrono, o que significa que retornaria um IAsyncEnumerable<T>.

 O código a seguir gera uma sequência de 0 a 19, aguardando 100 ms entre a geração de cada número:

'Bora pro' código...

Crie um projeto do tipo Console (.NET Core).

No exemplo a seguir temos um código que gera uma sequência numérica de 0 até 99 aguardando 100 milisegundos entre a geração de cada número:

A seguir defina o código abaixo na classe Program:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Cshp_AsyncStreams
{
    class Program
    {
        static void Main(string[] args)
        {
            imprimirSequenciaNumerica().Wait();
        }

        public static async IAsyncEnumerable<int> GerarSequenciaNumerica()
        {
            for (int i = 0; i < 100; i++)
            {
                //aguarda 100 ms
                await Task.Delay(100);
                yield return i;
            }
        }
        public static async Task imprimirSequenciaNumerica()
        {
            await foreach (var numero in GerarSequenciaNumerica())
            {
                Console.Write($"{numero}");
            }
        }       
    }
}

Observe que estamos enumerando a sequência usando um await na instrução foreach e estamos retornando yield return i. (a instrução yield return retornar cada elemento individualmente).

Nota:  Quando você usa a palavra-chave yield em uma instrução, você indica que o método, o operador ou o acessador get em que ela é exibida é um iterador. Usar yield para definir um iterador elimina a necessidade de uma classe adicional explícita (a classe que mantém o estado de uma enumeração, consulte IEnumerator<T> para obter um exemplo) ao implementar o padrão IEnumerable e IEnumerator para um tipo de coleção personalizado."

A instrução foreach foi estendida com funcionalidade assíncrona fazendo uso das interfaces assíncronas quando a palavra-chave await for usada. Com await foreach, um item é recuperado após o outro, sem bloquear a thread de chamada.

A interface IAsyncEnumerable define o método GetAsyncEnumerator que retorna um IAsyncEnumerator.

Isso é semelhante à implementação da interface IEnumerable. Devido à natureza assíncrona da interface, o método GetAsyncEnumerator define um parâmetro opcional do tipo CancellationToken para permitir o cancelamento antecipado.

Executando o projeto teremos:

E estamos conversados.

"E, se não há ressurreição de mortos, também Cristo não ressuscitou.
E, se Cristo não ressuscitou, logo é vã a nossa pregação, e também é vã a vossa fé."
1 Coríntios 15:13,14

Referências:


José Carlos Macoratti