Хобрук: Ваш путь к мастерству в программировании

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


  • элементы уже локальны, ждать больше нечего Что это значит? 15.05.2018
  • Вам нужен асинхронный перечислитель, который еще не поддерживает язык (вот предложение: github.com/dotnet/csharplang/blob/master/proposals/), но вы, конечно, можете использовать этот шаблон и без такой поддержки (без foreach, с более подробным синтаксисом). 15.05.2018
  • В конце концов я хотел создать функции расширения, такие как ToListAsync, ToArrayAsync, CountAsync, AnyAsync и т. д. Но для них мне нужен базовый IEnumerable. Я прочитаю ваше предложение и проверю, не стоит ли создать мою собственную EnumeratorAsync<TSource>. 15.05.2018

Ответы:


1

Ваша реализация шаблона async/await выглядит неправильно.

GetPageAsync подпись в порядке:

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

Теперь, если вы хотите вызвать этот метод асинхронно, вы должны использовать ключевое слово await, которое приостановит выполнение метода и вернет управление вызывающей стороне до тех пор, пока не будет готов результат GetPageAsync:

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

or

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

// Some other stuff

List<MyType> items = await getPageTask;

Но обратите внимание, что ключевое слово await можно использовать только внутри метода, который сам является async.

Возможно, я не понимаю всей идеи асинхронной модели вашего приложения, но в любом случае я бы рекомендовал взглянуть на это статья MSDN об асинхронном программировании с помощью async/await.

15.05.2018
  • Проблема в том, что я не знаю, сколько предметов хочет мой абонент. Он использует только FirstOrDefault? Будет ли он Take(2000)? Следовательно, я не хочу получать больше страниц, чем необходимо. Если он перечислит только несколько первых элементов, то одной страницы будет достаточно. Entity Framework имеет ListAsync, FirstOrDefaultAsync и т. д. Чтобы иметь возможность создавать подобные функции, мне нужна базовая асинхронная функция, которая возвращает IEnumerable 15.05.2018
  • Новые материалы

    Основы принципов S.O.L.I.D, Javascript, Git и NoSQL
    каковы принципы S.O.L.I.D? Принципы SOLID призваны помочь разработчикам создавать надежные, удобные в сопровождении приложения. мы видим пять ключевых принципов. Принципы SOLID были разработаны..

    Как настроить Selenium в проекте Angular
    Угловой | Селен Как настроить Selenium в проекте Angular Держите свое приложение Angular и тесты Selenium в одной рабочей области и запускайте их с помощью Mocha. В этой статье мы..

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..

    Настольный ПК как «одно кольцо, чтобы править всеми» домашних компьютеров
    Вид после 9 месяцев использования С настольных компьютеров все началось, но в какой-то момент они стали «серверами», и мы все перешли на ноутбуки. В прошлом году я столкнулся с идеей настольных..

    Расширенные методы безопасности для VueJS: реализация аутентификации без пароля
    Руководство, которое поможет вам создавать безопасные приложения в долгосрочной перспективе Безопасность приложений часто упускается из виду в процессе разработки, потому что основная..

    стройный-i18следующий
    Представляем стройную оболочку для i18next. Эта библиотека, основанная на i18next, заключает экземпляр i18next в хранилище svelte и отслеживает события i18next, такие как languageChanged,..