如何从任务枚举集合创建IAsyncEnumerable?
解决方案
你遇到的核心问题是:Select(async e => ...)会立即遍历整个watchlist并启动所有异步任务,所以所有GetMovieFromWatchlistEntry的调用会同时执行,和后续的Task.WhenAll无关。想要实现「逐个创建并等待任务」的延迟求值,不能从已有的moviePromises集合转换(因为任务已经启动了),需要直接用IAsyncEnumerable从源头控制任务的执行时机。
正确实现方式
直接编写异步迭代器方法,在迭代时才逐个启动并等待任务:
// 定义异步迭代器方法,返回IAsyncEnumerable<Movie> public async IAsyncEnumerable<Movie> GetMoviesFromWatchlistAsync() { foreach (var entry in this.watchlist) { // 每次迭代时才调用并等待当前任务完成 var movie = await this.GetMovieFromWatchlistEntry(entry); yield return movie; } }
然后用await foreach遍历执行:
await foreach (var movie in GetMoviesFromWatchlistAsync()) { // do stuff }
为什么原来的方式不行?
Select(async e => await this.GetMovieFromWatchlistEntry(e))会立即枚举整个watchlist,对每个元素执行lambda表达式——而async lambda会立即启动异步任务并返回Task<Movie>,所以所有任务在Select执行时就已经同时跑起来了,后续的Task.WhenAll只是等待它们全部完成而已,无法控制逐个执行。
关于从现有moviePromises转换的说明
如果硬要从已有的IEnumerable<Task<Movie>>转换为IAsyncEnumerable,可以写如下方法:
public async IAsyncEnumerable<Movie> TasksToAsyncEnumerable(IEnumerable<Task<Movie>> tasks) { foreach (var task in tasks) { yield return await task; } }
但这种方式无法改变任务已经同时启动的事实,达不到你想要的延迟求值、逐个执行的效果,因此不推荐。
内容的提问来源于stack exchange,提问作者codymanix
相关产品推荐
相关产品推荐

