如何等待IAsyncEnumerable <>的所有结果?

时间:2019-11-18 13:00:39

标签: c# c#-8.0 iasyncenumerable

我正在修补C#8.0中的新IAsyncEnumerable<T>东西。假设我要在某个地方使用某种方法:

public IAsyncEnumerable<T> SomeBlackBoxFunctionAsync<T>(...) { ... }

我知道我可以将其与await foreach...语法一起使用。但是,可以说,我的使用者需要从该函数获得 all 结果,然后才能继续。在继续之前等待所有结果的最佳语法是什么?换句话说,我希望能够执行以下操作:

// but that extension - AllResultsAsync() - doesn't exist :-/
List<T> myList = await SomeBlackBoxFunctionAsync<T>().AllResultsAsync(); 

正确的方法是什么?

3 个答案:

答案 0 :(得分:5)

首先警告:按照定义,异步流可​​能永远不会结束,并且会继续产生结果,直到应用程序终止。已经在SignalR或gRPC中使用了 。轮询循环也以这种方式工作。

在异步流上使用ToListAsync可能会带来意想不到的后果。


像这样的操作员已经可以通过System.Linq.Async软件包获得。

可以通过ToListAsync使用整个流。该代码*看似简单,但隐藏了一些有趣的问题:

public static ValueTask<List<TSource>> ToListAsync<TSource>(this IAsyncEnumerable<TSource> source, CancellationToken cancellationToken = default)
{
    if (source == null)
        throw Error.ArgumentNull(nameof(source));

    if (source is IAsyncIListProvider<TSource> listProvider)
        return listProvider.ToListAsync(cancellationToken);

    return Core(source, cancellationToken);

    static async ValueTask<List<TSource>> Core(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
    {
        var list = new List<TSource>();

        await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
        {
            list.Add(item);
        }

        return list;
    }
}

首先,它返回一个ValueTask。其次,它确保观察到取消操作并使用ConfigureAwait(false)来防止死锁。最后,如果源已经提供了自己的ToListAsync实现,则操作员将遵循此实现。

答案 1 :(得分:4)

基于@DmitryBychenko的评论,我写了一个扩展名以求得到我想要的东西:

    public static async Task<ICollection<T>> AllResultsAsync<T>(this IAsyncEnumerable<T> asyncEnumerable)
    {
        if (null == asyncEnumerable)
            throw new ArgumentNullException(nameof(asyncEnumerable));  

        var list = new List<T>();
        await foreach (var t in asyncEnumerable)
        {
            list.Add(t);
        }

        return list;
    }

我只是有点惊讶,这不是C#8.0本身提供的……似乎很明显的需求。

答案 2 :(得分:1)

作为一种选择,您可以使用ToArrayAsync中定义的System.Linq.Async扩展方法,GitHub的来源是here

public static ValueTask<TSource[]> ToArrayAsync<TSource>(this IAsyncEnumerable<TSource> source, CancellationToken cancellationToken = default)

根据定义,它扩展了IAsyncEnumerable接口