IAsyncEnumerable

Resumo : neste tutorial, você aprenderá como lidar com fluxos assíncronos usando a IAsyncEnumerable<T>interface.

Introdução à interface IAsyncEnumerable

Suponha que você tenha um método assíncronoGetMessage() que retorne uma mensagem após um segundo:

async Task<string> GetMessage(int n)
{
    await Task.Delay(1000);
    return $"Message #{n}";
}Linguagem de código:  C#  ( cs )

Para definir um método que retorne uma sequência de mensagens com base no GetMessage()método, você usa a IAsyncEnumerable<T>interface:

async IAsyncEnumerable<string> GetMessages(int max)
{
    for (var i = 1; i <= max; i++)
    {
        var message = await GetMessage(i);
        yield return message;
    }
}Linguagem de código:  C#  ( cs )

O GetMessages()método chama o GetMessage()método para gerar uma sequência de mensagens. Ele retorna uma IAsyncEnumerableinterface <T> que itera as sequências de forma assíncrona.

O IAsyncEnumerable<T>possui o GetAsyncEnumerator()método que retorna um enumerador que itera de forma assíncrona pela coleção:

IAsyncEnumerator GetAsyncEnumerator(
    CancellationToken cancellationToken = default
);Linguagem de código:  C#  ( cs )

O GetAsyncEnumerator()método possui um parâmetro CancellationTokenque permite cancelar a iteração assíncrona. Ele retorna uma IAsyncEnumerator<T>interface que possui os métodos Currente MoveNextAsync():

Membro IEnumerable<T> IAsyncEnumerator<T>
Método GetEnumerator GetAsyncEnumerator
Membro IEnumerador<T> IAsyncEnumerator<T>
Propriedade Current Current
Método MoveNext() MoveNextAsync()

Para consumir um IAsyncEnumerator, você usa a await foreachinstrução em vez da foreachinstrução:

await foreach (var message in GetMessages(5))
{
    WriteLine(message);
}Linguagem de código:  C#  ( cs )

Saída:

Message #1
Message #2
Message #3
Message #4
Message #5Linguagem de código:  C#  ( cs )

O programa exibe cinco mensagens a cada segundo.

Junte tudo.

using static System.Console;


await foreach (var message in GetMessages(5))
{
    WriteLine(message);
}


async IAsyncEnumerable<string> GetMessages(int max)
{
    for (var i = 1; i <= max; i++)
    {
        var message = await GetMessage(i);
        yield return message;
    }
}

async Task<string> GetMessage(int n)
{
    await Task.Delay(1000);
    return $"Message #{n}";
}Linguagem de código:  C#  ( cs )

Usando a interface IAsyncEnumerable com CancellationToken

O exemplo a seguir mostra como utilizar o CancellationTokenpara solicitar o cancelamento de uma operação:

using System.Runtime.CompilerServices;
using static System.Console;

var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(3));

try
{
    await foreach (var message in GetMessages(5, cts.Token))
    {
        WriteLine(message);
    }
}
catch(TaskCanceledException ex)
{
    WriteLine(ex.Message);
}

async IAsyncEnumerable<string> GetMessages(int max, [EnumeratorCancellation] CancellationToken token = default)
{
    for (var i = 1; i <= max; i++)
    {
        var message = await GetMessage(i, token);
        yield return message;
    }
}

async Task<string> GetMessage(int n, CancellationToken token=default)
{
    await Task.Delay(1000, token);
    return $"Message #{n}";
}Linguagem de código:  C#  ( cs )

Saída:

Message #1
Message #2
A task was canceled.Linguagem de código:  C#  ( cs )

Como funciona.

Primeiro, crie um CancellationTokenSourceobjeto e agende uma operação de cancelamento após 3 segundos:

var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(3));Linguagem de código:  C#  ( cs )

Em segundo lugar, adicione os CancellationTokenmétodos GetMessage()e GetMessages(). No GetMessage()método, passe o token para o Task.Delaymétodo ().

Uso prático da interface IAsyncEnumerable

O programa a seguir ilustra como recuperar o conteúdo de uma lista URLse gravar o comprimento de cada texto baixado no console usando IAsyncEnumerablee HttpClient:

using static System.Console;

var urls = new List<string>()
{
    "https://datatracker.ietf.org/doc/html/rfc791",
    "https://datatracker.ietf.org/doc/html/rfc1180",
    "https://datatracker.ietf.org/doc/html/rfc2616"
};


await foreach (var result in GetTexts(urls))
{
    WriteLine(result?.Length);
}

static async IAsyncEnumerable<string?> GetTexts(List<string> urls)
{

    foreach (var url in urls)
    {
        var text = await Download(url);
        yield return text;
    }
}

static async Task<string?> Download(string url)
{
    try
    {
        using var client = new HttpClient();
        
        var response = await client.GetAsync(url);
        response.EnsureSuccessStatusCode();
        var text = await response.Content.ReadAsStringAsync();
        return text;
    }
    catch (HttpRequestException ex)
    {
        WriteLine(ex.Message);
        return null;
    }
}Linguagem de código:  C#  ( cs )

Saída:

126891
102930
580146Linguagem de código:  C#  ( cs )

Resumo

  • Use a instrução IAsyncEnumerable<T>e await foreachpara iterar uma sequência de itens de forma assíncrona.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *