【问题标题】:return IEnumerable as result of async function calls作为异步函数调用的结果返回 IEnumerable
【发布时间】:2018-05-15 07:57:12
【问题描述】:

我有一个获取一系列项目的函数:

List<MyType> GetPage(int pageNr) {...}

要获取所有可用项目,您可以执行以下操作:

IEnumerable<MyType> FetchMyItems()
{
    int pageNr = 0;
    var fetchedItems = GetPage(pageNr);
    while (fetchedItems.Any()
    {   // there are items to return
        // yield return the fetched items: 
        foreach (var fetchedItem in fetchedItems)
            yield return fetchedItems;

        // get the next page
         ++pageNr;
         fetchedItems = GetPage(pageNr)
    }
}

使用此方法,我的用户将不再需要获取每页的项目,如果他们只需要几个项目,则只会获取第一页。当他们需要更多项目时,将自动获取更多页面。缺点当然是我有时会多取一些不会用到的物品。

我想要一个异步版本

所以我有以下GetPage:

async Task<List<MyType>> GetPageAsync(int pageNr) {...}

根据我对 IEnumerable 的了解,它不会创建结果序列,它只会创建枚举此序列的可能性。

这种枚举是使用 'foreach' 显式完成的,或隐式使用 'ToList'、'ToArray' 等 LINQ 函数,但也使用 'FirstOrDefault'、'Any'、Sum 等函数。

var fetchItemTasks = FetchMyItemsAsync();
foreach (var fetchItemTask in fetchedItemTasks)
{
      MyType fetchedItem = await fetchItemTask;
      Process(fetchedItem);
}

或者如果你做这个低级:

var myitemsEnumerable = fetchMyItemsAsync();
// don't await yet, only create the possibility to enumerate

var enumerator = myItemsEnumerable.GetEnumerator();
while (enumerator.MoveNext())
{   // there is an item available in current:
    Task<MyType> task = enumerator.Current;
    return task;
}

看这个,看来这个函数不应该返回Task&lt;IEnumerable&lt;MyType&gt;&gt;而是IEnumerable&lt;Task&lt;MyType&gt;&gt;,如果你想访问一个元素你必须等待这个。

因此,返回枚举等待项的可能性的函数本身并不是异步的。您无需等待可枚举,无需访问慢速提供程序(如数据库)即可创建此可枚举。可枚举序列中的项是可等待的:

IEnumerable<Task<MyType>> FetchMyItems()
{
    int pageNr = 0;
    Task<List<MyType>> fetchItemTask = GetPageAsync(pageNr);

现在呢?如果我await fetchItemTask,这些项目已经是本地的,没有什么可以等待的了。如果我fetchItemTask.Wait(),功能块。

【问题讨论】:

  • 这些项目已经是本地的,没有什么可以等待的了这是什么意思?
  • 你需要的是异步枚举器,它还没有语言支持(这里有一个建议:github.com/dotnet/csharplang/blob/master/proposals/…),但是你当然可以在没有这种支持的情况下使用这种模式(没有 foreach,还有更多详细的语法)。
  • 最后我想创建扩展函数,如ToListAsync、ToArrayAsync、CountAsync、AnyAsync 等。但对于它们,我需要一个基本的 IEnumerable。我会阅读您的建议并检查是否可以创建我自己的 EnumeratorAsync&lt;TSource&gt;

标签: c# async-await ienumerable


【解决方案1】:

您对 async/await 模式的实现看起来不正确。

GetPageAsync签名没问题:

async Task<List<MyType>> GetPageAsync(int pageNr) {...}

现在,如果你想异步调用这个方法,你必须使用await关键字,它会暂停执行方法并将控制权返回给它的调用者,直到GetPageAsync结果准备好:

List<MyType> items = await GetPageAsync(pageNr);

或

Task<List<MyType>> getPageTask = GetPageAsync(pageNr);

// Some other stuff

List<MyType> items = await getPageTask;

但请注意,await 关键字只能在本身为 async 的方法内使用。

可能我没有意识到您的应用程序异步模型的全部想法,但无论如何,我建议您先看看这个MSDN article on asynchronous programming with async/await。

【讨论】:

  • 问题是,我不知道我的来电者想要多少物品。他只使用FirstOrDefault吗?他会Take(2000)吗?因此,我不想获取比需要更多的页面。如果他只列举前几项,那么获取一页就足够了。实体框架有` ListAsync, FirstOrDefaultAsync` 等。为了能够创建类似的函数,我需要一个返回 IEnumerable 的基本异步函数
猜你喜欢
  • 2019-09-30
  • 2022-12-22
  • 2020-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-27
  • 1970-01-01
  • 2017-04-07
相关资源
最近更新 更多