Совместимы ли обычные блоки итератора (т.е. «yield return») с «async» и «await»?
Это дает хорошее представление о том, что я пытаюсь сделать:
async Task<IEnumerable<Foo>> Method(String [] Strs)
{
// I want to compose the single result to the final result, so I use the SelectMany
var finalResult = UrlStrings.SelectMany(link => //i have an Urlstring Collection
await UrlString.DownLoadHtmlAsync() //download single result; DownLoadHtmlAsync method will Download the url's html code
);
return finalResult;
}
Однако я получаю сообщение об ошибке компилятора «невозможно загрузить строку сообщения из ресурсов».
Вот еще одна попытка:
async Task<IEnumerable<Foo>> Method(String [] Strs)
{
foreach(var str in strs)
{
yield return await DoSomethingAsync( str)
}
}
Но опять же компилятор возвращает ошибку: «не удалось загрузить строку сообщения из ресурсов».
Вот настоящий программный код в моем проекте
Это очень полезно, когда у меня есть задача списка, эта задача может быть загружена HTML с URL-адреса, и я использую синтаксис «yield return await task», результат такой, как я IEnumerable<Foo>
. Я не хочу писать этот код:
async Task<IEnumerable<String>> DownLoadAllURL(String [] Strs)
{
List<Foo> htmls= new ...
foreach(var str in strs)
{
var html= await DownLoadHtmlAsync( str)
htmls.Add(item)
}
return htmls;
}
Но похоже, что я должен.
Спасибо за любую помощь.
c#
async-await
c#-5.0
iasyncenumerable
Цзянчжэнь
источник
источник
IAsyncEnumerator<T>
тип, определенный Арне.Ответы:
То, что вы описываете, можно выполнить с помощью
Task.WhenAll
метода. Обратите внимание, как код превращается в простой однострочник. Что происходит, так это то, что каждый отдельный URL-адрес начинает загрузку, а затемWhenAll
используется для объединения этих операций в одну,Task
которую можно ожидать.источник
async
из метода и делайтеreturn Task.WhenAll
напрямую.urls.Select(DownloadHtmlAsync)
Is it possible to await yield?
вы только что нашли обходной путь для этого одного варианта использования. Не ответил на общий вопрос.Task<string[]>
поскольку это будет означать, что вы больше не возвращаете итератор с отложенным выполнением, т.е. все URL-адреса загружаются.tl; dr Итераторы, реализованные с yield, являются блокирующей конструкцией, поэтому на данный момент await и yield несовместимы.
Длинный Поскольку итерация по объекту
IEnumerable
является блокирующей операцией, вызов метода, помеченного какasync
, по-прежнему будет выполнять его блокирующим образом, поскольку он должен ждать завершения этой операции.Ожидание
Method
смешивает смыслы. Вы хотите подождать, покаTask
не появится,IEnumerable
а затем заблокировать его повторение? Или вы пытаетесь дождаться каждого значенияIEnumerable
?Я предполагаю, что второе - это желаемое поведение, и в этом случае существующая семантика Iterator не будет работать.
IEnumerator<T>
Интерфейс в основномЯ игнорирую,
Reset()
поскольку это не имеет смысла для последовательности асинхронных результатов. Но вам понадобится что-то вроде этого:Конечно, это
foreach
тоже не сработает, и вам придется выполнять итерацию вручную следующим образом:источник
foreach
поддержку вашей гипотезыIAsyncEnumerator<T>
?В соответствии с новыми функциями C # 8.0 ( ссылка №1 и ссылка №2 ) у нас будет
IAsyncEnumerable<T>
поддержка интерфейса, которая позволит реализовать вашу вторую попытку. Это будет выглядеть так:Мы можем добиться того же поведения на C # 5, но с другой семантикой:
Ответ Брайана Гидеона подразумевает, что вызывающий код будет асинхронно получать набор результатов, которые были получены параллельно. Приведенный выше код подразумевает, что вызывающий код будет получать результаты, как из потока, один за другим в асинхронном режиме.
источник
Я знаю, что слишком поздно с ответом, но вот еще одно простое решение, которое может быть достигнуто с помощью этой библиотеки:
GitHub: https://github.com/tyrotoxin/AsyncEnumerable
NuGet.org: https: //www.nuget .org / packages / AsyncEnumerator /
Это намного проще, чем Rx.
источник
Эта функция будет доступна в C # 8.0. https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/
Из MSDN
Асинхронные потоки
Функция async / await в C # 5.0 позволяет получать (и создавать) асинхронные результаты в виде простого кода без обратных вызовов:
Это не так полезно, если вы хотите получать (или производить) непрерывные потоки результатов, например, которые вы можете получить от устройства IoT или облачной службы. Для этого существуют асинхронные потоки.
Мы представляем IAsyncEnumerable, чего и следовало ожидать; асинхронная версия IEnumerable. Язык позволяет вам ждать foreach над ними, чтобы использовать их элементы, и возвращать им return для создания элементов.
источник
Был план сделать
https://github.com/dotnet/csharplang/issues/43
Но в настоящее время это невозможно
источник
Прежде всего, имейте в виду, что работа с Async еще не завершена. Команде C # еще предстоит пройти долгий путь до выпуска C # 5.
При этом, я думаю, вы можете захотеть собрать задачи, которые запускаются в
DownloadAllHtml
функции, по-другому.Например, вы можете использовать что-то вроде этого:
Не то чтобы
DownloadAllUrl
функция НЕ была асинхронным вызовом. Но вы можете реализовать асинхронный вызов в другой функции (т.е.DownloadHtmlAsync
).Целевая Параллельная библиотека имеет
.ContinueWhenAny
и.ContinueWhenAll
функцию.Это можно использовать так:
источник
Task<IEnumerable<Task<string>>> DownloadAllUrl
. Или, если вам нужны действия «нижнего колонтитула»IEnumerable<Task>
. Например, gist.github.com/1184435async
материал будет закончен и C # 5.0 будет выпущен, это может быть обновлен.К сожалению, Yield не работает с await. Но это то, для чего нужен Rx. Посетите https://msdn.microsoft.com/library/hh242985
источник
Это решение работает, как ожидалось. Обратите внимание на
await Task.Run(() => enumerator.MoveNext())
часть.источник