Читаем все страницы API через IAsyncEnumerable в C#!
Многие API отдают данные порциями. Если собрать все страницы в List сразу, код быстро разрастается, а юзер не увидит ни одной записи до конца загрузки.
IAsyncEnumerable позволяет отдавать элементы по мере чтения следующей страницы. Вызывающий код использует await foreach и не знает, сколько запросов понадобилось внутри.
record Page(IReadOnlyList Items, string? NextCursor);
Метод клиента пусть возвращает страницу и курсор для следующего запроса. Null означает, что сервер больше ничего не отдаёт.
Task GetPageAsync(
string? cursor, CancellationToken token);
В итераторе сохраняем текущий курсор. После каждого ответа возвращаем заказы по одному и только потом запрашиваем следующую страницу.
string? cursor = null;
do {
var page = await GetPageAsync(cursor, token);
foreach (var order in page.Items) yield return order;
cursor = page.NextCursor;
} while (cursor is not null);
Юзер может остановиться в любой момент. CancellationToken важно передавать в сетевой вызов, чтобы отмена не ждала лишнюю страницу.
await foreach (var order in ReadOrders(token))
{
await SaveAsync(order, token);
}
Такой подход полезен для импорта каталога, обработки событий и синхронизации данных. Память не растёт вместе с общим количеством страниц, а обработка начинается сразу после первого ответа.
➡️ C# Ready | #практика
Многие API отдают данные порциями. Если собрать все страницы в List сразу, код быстро разрастается, а юзер не увидит ни одной записи до конца загрузки.
IAsyncEnumerable позволяет отдавать элементы по мере чтения следующей страницы. Вызывающий код использует await foreach и не знает, сколько запросов понадобилось внутри.
record Page(IReadOnlyList Items, string? NextCursor);
Метод клиента пусть возвращает страницу и курсор для следующего запроса. Null означает, что сервер больше ничего не отдаёт.
Task GetPageAsync(
string? cursor, CancellationToken token);
В итераторе сохраняем текущий курсор. После каждого ответа возвращаем заказы по одному и только потом запрашиваем следующую страницу.
string? cursor = null;
do {
var page = await GetPageAsync(cursor, token);
foreach (var order in page.Items) yield return order;
cursor = page.NextCursor;
} while (cursor is not null);
Юзер может остановиться в любой момент. CancellationToken важно передавать в сетевой вызов, чтобы отмена не ждала лишнюю страницу.
await foreach (var order in ReadOrders(token))
{
await SaveAsync(order, token);
}
Такой подход полезен для импорта каталога, обработки событий и синхронизации данных. Память не растёт вместе с общим количеством страниц, а обработка начинается сразу после первого ответа.
➡️ C# Ready | #практика