[英]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.