首页 > 解决方案 > 作为异步函数调用的结果返回 IEnumerable

问题描述

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

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”显式完成的,或隐式使用 LINQ 函数(如“ToList”、“ToArray”)以及“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<IEnumerable<MyType>>但是IEnumerable<Task<MyType>>,如果你想访问一个元素,你将不得不等待这个。

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

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

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

标签: c#async-awaitienumerable

解决方案


您对 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

可能我没有意识到您的应用程序异步模型的全部想法,但无论如何,我建议您先看看这篇关于使用 async/await 进行异步编程的 MSDN 文章


推荐阅读