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 IAsyncEnumerable
interface <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 CancellationToken
que permite cancelar a iteração assíncrona. Ele retorna uma IAsyncEnumerator<T>
interface que possui os métodos Current
e 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 foreach
instrução em vez da foreach
instruçã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 #5
Linguagem 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 CancellationToken
para 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 CancellationTokenSource
objeto 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 CancellationToken
métodos GetMessage()
e GetMessages()
. No GetMessage()
método, passe o token para o Task.Delay
método ().
Uso prático da interface IAsyncEnumerable
O programa a seguir ilustra como recuperar o conteúdo de uma lista URLs
e gravar o comprimento de cada texto baixado no console usando IAsyncEnumerable
e 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
580146
Linguagem de código: C# ( cs )
Resumo
- Use a instrução
IAsyncEnumerable<T>
eawait foreach
para iterar uma sequência de itens de forma assíncrona.