вернуть IEnumerable в результате вызовов асинхронных функций - PullRequest
0 голосов
/ 15 мая 2018

У меня есть функция, которая получает последовательность элементов:

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, то предметы уже локальные, больше ждать нечего. Если я fetchItemTask.Wait(), функциональные блоки.

1 Ответ

0 голосов
/ 15 мая 2018

Ваша реализация шаблона 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 об асинхронном программировании с использованием async / await .

...