How to await all results from an IAsyncEnumerable<>?

Based on @DmitryBychenko's comment, I wrote an extension to do want I want:

    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;
    }

I'm just a little surprised this wasn't shipped natively with C# 8.0...it seems like a pretty obvious need.


A warning first: by definition, an async stream may never end and keep producing results until the application terminates. This is already used e.g. in SignalR or gRPC. Polling loops also work this way.

As such, using ToListAsync on an async stream may have unintended consequences!


Operators like this are already available through the System.Linq.Async package.

Consuming the entire stream is available through ToListAsync. The code is deceptively simple, but hides a few interesting issues :

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;
    }
}

First of all, it returns a ValueTask. Second, it ensures cancellation is observed and ConfigureAwait(false) is used, to prevent deadlocks. Finally, if the source already offers its own ToListAsync implementation via IAsyncIListProvider, the operator defers to that.

It's also interesting to note that while the IAsyncIListProvider interface is public, it's only implemented by internal and private classes within System.Linq.Async.