【问题标题】:How to concat async enumerables?如何连接异步枚举?
【发布时间】:2015-02-23 16:10:56
【问题描述】:

我有一个具有这种返回类型的方法:

public async Task<IEnumerable<T>> GetAll()

它进一步进行了一些异步调用(未知数),每个调用都返回一个可枚举 T 的任务,然后想要连接结果以返回。

var data1 = src1.GetAll();
var data2 = src2.GetAll();
var data3 = src3.GetAll(); //and so on

现在很容易等待所有并连接结果以生成单个可枚举,但我希望可枚举在第一个调用返回后立即可用,如果有任何调用,则可能等待调用者/枚举器当可用结果用完时仍处于等待状态。

我是否必须为此手动滚动一个 concat,以解决当它被包裹在任务 中时缺少枚举器支持的问题?或者在 TPL 或其他地方已经有一个库调用可以帮助我。我确实看过 IX,但它仍处于实验版本,不想折叠它。

在旁注中,我正在尝试反模式吗?我可以想到一个复杂性,异常处理 - 从调用者的角度来看,调用可以成功完成,他开始使用可枚举但它可能会在中途爆炸......

【问题讨论】:

  • (作为旁注)您的模式似乎是不对称的:代码等待第一个序列准备好,但不等待其余的。跨度>
  • 也许你真的需要IObservable&lt;T&gt;?在这种情况下,您可以使用Observable.Concat
  • @Vlad:好点子。当我研究 Ix / Rx 时,我确实想到了这一点,我同意,反应式确实更适合这种类型的用例。但整体场景和应用程序是基于拉动的,我不想放弃 Rx 锤子只是为了从可观察的结果中返回可枚举。

标签: c# asynchronous async-await task-parallel-library ienumerable


【解决方案1】:

有一个名为Async Enumerable 的现有项目正好解决了这个问题。

你可以很容易地使用它。

例如:

IAsyncEnumerable<string> GetAsyncAnswers()
{
    return AsyncEnum.Enumerate<string>(async consumer =>
    {
        foreach (var question in GetQuestions())
        {
            string theAnswer = await answeringService.GetAnswer(question);
            await consumer.YieldAsync(theAnswer);
        }
    });
}

这会暴露一个IAsyncEnumerable&lt;string&gt;,一旦GetAnswer 返回就会产生。您可以在内部公开IAsyncEnumerable&lt;T&gt;,并在内部调用GetAll

我在尝试什么反模式?我能想到一个并发症, 异常处理——从调用方来说,调用可以完成 成功,他开始使用可枚举但它可能会爆炸 中途...

我不会这么说。这确实存在潜在问题,例如在其中一个等待期间内部发生异常,但这也可能潜在发生在任何IEnumerable&lt;T&gt; 内部。异步序列是当今新兴异步 API 的现实所需要的东西。

【讨论】:

  • 谢谢。标记为答案。我没有导入该库,而是通过一个连接器以获取下一个可枚举的函数的方式,在与您的示例类似的两行中滚动我自己的库。如果我得到更广泛的用例,也许我会再看看这个库。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-23
  • 1970-01-01
  • 2018-02-23
  • 2021-07-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多