想象一下,我们可以按照命令式风格将惰性枚举(yield return)与异步方法结合起来。这种组合称为Async Streams。这是C# 8中新提出的功能。这个新功能为我们提供了一种很好的技术来解决拉取式编程模型问题,例如从网站下载数据或从文件或数据库中读取记录。
让我们尝试使用当前的C# 版本。我将async关键字添加到SumFromOneToCountYield方法中,如下所示。
图-2 组合使用async关键字和yield发生错误
我们试着将async添加到SumFromOneToCountYield,但直接出现错误,如上所示!
让我们试试别的吧。我们可以将IEnumerable放入任务中并删除yield关键字,如下所示:
static async Task<IEnumerable<int>> SumFromOneToCountTaskIEnumerable(int count) { ConsoleExt.WriteLine("SumFromOneToCountAsyncIEnumerable called!"); var collection = new Collection<int>(); var result = await Task.Run(() => { var sum = 0; for (var i = 0; i <= count; i++) { sum = sum + i; collection.Add(sum); } return collection; }); return result; }调用方法:
const int count = 5; ConsoleExt.WriteLine("SumFromOneToCountAsyncIEnumerable started!"); var scs = await SumFromOneToCountTaskIEnumerable(count); ConsoleExt.WriteLine("SumFromOneToCountAsyncIEnumerable done!"); foreach (var sc in scs) { // 这不是我们想要的,结果将作为块返回!!!! ConsoleExt.WriteLine($"AsyncIEnumerable Result: {sc}"); } ConsoleExt.WriteLine("################################################"); ConsoleExt.WriteLine(Environment.NewLine);输出:
可以看到,我们异步计算所有的内容,但仍然存在一个问题。结果(所有结果都在集合中累积)作为一个块返回,但这不是我们想要的惰性行为,我们的目标是将惰性行为与异步计算风格相结合。
为了实现所需的行为,你需要使用外部库,如Ix(Rx的一部分),或者你必须使用新提出的C#特性Async Streams。
回到我们的代码示例。我使用了一个外部库来显示异步行为。
static async Task ConsumeAsyncSumSeqeunc(IAsyncEnumerable<int> sequence) { ConsoleExt.WriteLineAsync("ConsumeAsyncSumSeqeunc Called"); await sequence.ForEachAsync(value => { ConsoleExt.WriteLineAsync($"Consuming the value: {value}"); // 模拟延迟! Task.Delay(TimeSpan.FromSeconds(1)).Wait(); }); } static IEnumerable<int> ProduceAsyncSumSeqeunc(int count) { ConsoleExt.WriteLineAsync("ProduceAsyncSumSeqeunc Called"); var sum = 0; for (var i = 0; i <= count; i++) { sum = sum + i; // 模拟延迟! Task.Delay(TimeSpan.FromSeconds(0.5)).Wait(); yield return sum; } }