简体   繁体   中英

return IEnumerable as result of async function calls

I have a function that gets a sequence of items:

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

To get all available items you could do something like:

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)
    }
}

Using this method, my users won't have to fetch the items per page anymore, if they need only a few items only the first page will be fetched. More pages will be fetched automatically when they need more items. The disadvantage is of course that I sometimes fetch a few more items that won't be used.

I want an async version for this

So I have the following GetPage:

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

From what I understand from IEnumerable, is that it doesn't create the resulting sequence, it only creates the possibility to enumerate this sequence.

This enumerating is done explicitly using 'foreach', or implicitly using LINQ functions like 'ToList', 'ToArray', but also functions like 'FirstOrDefault', 'Any', Sum .

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

Or if you do this low level:

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;
}

Looking at this, It seems that the function should not return Task<IEnumerable<MyType>> but IEnumerable<Task<MyType>> , if you want to access an element you'll have to await for this.

Therefore, the function that returns the possibility to enumerate over awaitable items is not async for itself. You don't await for the enumerable, this enumerable can be created without accessing slow providers like a database. The items in the enumerable sequence are awaitable:

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

And now? If I await fetchItemTask , the items are already local nothing to await for anymore. If I fetchItemTask.Wait() , the function blocks.

Your implementation of async/await pattern doesn't look correct.

GetPageAsync signature is fine:

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

Now, if you want to call this method asynchronously you have to use await keyword, which will suspend the executing method and return control to its caller until the GetPageAsync result is ready:

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

or

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

// Some other stuff

List<MyType> items = await getPageTask;

But note that await keyword can only be used inside a method that is itself async .

Probably I don't realize the whole idea of your application async model, but in any case, I would recommend having a look at this MSDN article on asynchronous programming with async/await first.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM