将 IEnumerable<Task<T>> 转换为 IAsyncEnumerable<T> 的最佳方法?

问题描述 投票:0回答:1

这是我能想到的最好的:

public static async IAsyncEnumerable<T> AsAsyncEnumerable<T>(this IEnumerable<Task<T>> tasks)
{
    foreach (Task<T> task in tasks)
    {
        yield return await task;
    }
}

有更好/推荐的方法吗?

c# async-await
1个回答
0
投票

有更好/推荐的方法吗?

其实不然。您的实施几乎已经尽善尽美了。您可以考虑添加取消支持,如下所示:

public static async IAsyncEnumerable<T> ToAsyncEnumerable<T>(
    this IEnumerable<Task<T>> source,
    [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
    ArgumentNullException.ThrowIfNull(source);
    foreach (Task<T> task in tasks)
    {
        yield return await task.ConfigureAwait(false);
        cancellationToken.ThrowIfCancellationRequested();
    }
}

但这并不是真正需要的。如果消费者想要取消枚举,他们可以使用

await foreach
break
退出消费
return
循环。如果
CancellationToken
会影响等待的任务,那么它会很有用,但任务的创建被委托给源
IEnumerable<Task<T>>
,它超出了
ToAsyncEnumerable
方法的边界。因此,我宁愿跳过中间
IEnumerable<Task<T>>
的创建,并将底层
IEnumerable<TSource>
直接投影到
IAsyncEnumerable<TResult>
:

public static async IAsyncEnumerable<TResult> SelectAsync<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, CancellationToken, ValueTask<TResult>> selector,
    [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
    ArgumentNullException.ThrowIfNull(source);
    ArgumentNullException.ThrowIfNull(selector);
    foreach (TSource item in source)
    {
        yield return await selector(item, cancellationToken).ConfigureAwait(false);
    }
}

使用示例:

IAsyncEnumerable<Response> responses = requests
    .SelectAsync(async (request, ct) => await Get(request, ct));

await foreach (Response response in responses.WithCancellation(cancellationToken))
{
    //...
}
© www.soinside.com 2019 - 2024. All rights reserved.