![](/img/trans.png)
[英]Clarification on how IAsyncEnumerable works with ASP.NET Web API
[英]How GroupBy works with IAsyncEnumerable?
我已經在有分頁的HttpClient
請求中實現IAsyncEnumerable
,但我還需要對它們進行GroupBy
。 所以我實現了如下代碼;
public class Item
{
public int Id {get; set;}
public string Name {get; set;}
}
public async IAsyncEnumerable<Item> GetItems()
{
while(hasMorePage)
{
// ... get paginated items
foreach(var item in paginatedItems)
{
yield return item;
}
}
}
// should find most repeated item(by Id) and count of them.
public async Task GroupItems()
{
IAsyncEnumerable<Item> items = GetItems();
//IAsyncGrouping
await foreach(var item in items.GroupBy(i => i.Id).
OrderByDescendingAwait(i => i.CountAsync()).
Take(10))
{
Console.WriteLine(item.Key.ToString() + (await item.CountAsync()).ToString());
}
}
正如我所料,這段代碼工作得很好。 但我想了解GroupBy
是如何在這里工作的,因為它應該有所有按 id 分組的項目有什么我想念的嗎? 或者有什么我可以重構性能的嗎?
首先,評論中鏈接的 ALinq repo 與 .NET 的 IAsyncEnumerable 或 System.Linq.Async 無關。 這是一個 8 年歷史的存儲庫,甚至沒有針對 .NET 核心。 System.Linq.Async 由為 .NET 構建 Reactive Excetions 的同一團隊維護,其代碼位於同一 Github 存儲庫中
其次,不清楚需要解釋什么行為。
GroupBy
會阻止嗎? 不,它沒有。GroupBy
是否必須在產生結果之前消耗整個源? 是的,它確實。如果您有長時間運行的 stream 事件,則必須等到 stream 結束才能獲得任何結果。 這是因為 GroupBy 在其分配階段計算分組,然后在其迭代階段返回它們
protected override async ValueTask<bool> MoveNextCore()
{
switch (_state)
{
case AsyncIteratorState.Allocated:
_lookup = await Internal.Lookup<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, _cancellationToken).ConfigureAwait(false);
_enumerator = _lookup.ApplyResultSelector(_resultSelector).GetEnumerator();
_state = AsyncIteratorState.Iterating;
goto case AsyncIteratorState.Iterating;
case AsyncIteratorState.Iterating:
if (_enumerator!.MoveNext())
{
_current = _enumerator.Current;
return true;
}
await DisposeAsync().ConfigureAwait(false);
break;
}
return false;
}
如果您想處理事件流,您應該查看 Rx.NET,它由創建 System.Linq.Async 的同一團隊構建。 在 Rx.NET 中,當遇到新的鍵值時, GroupBy將發出一個新組 stream:
請注意,Rx.NET 的GroupBy
實際上按分組鍵對事件 stream 進行分區,並發出流而不是分組。 訂閱者將訂閱這些流並處理他們的事件。 此聚合示例演示了這一點:
var source = Observable.Interval(TimeSpan.FromSeconds(0.1)).Take(10);
var group = source.GroupBy(i => i % 3);
group.Subscribe(
grp =>
grp.Min().Subscribe(
minValue =>
Console.WriteLine("{0} min value = {1}", grp.Key, minValue)),
() => Console.WriteLine("Completed"));
如果您需要處理長時間運行的 IAsyncEnumerable<> stream 您可以使用ToObservable
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.