Is it possible to "await yield return DoSomethingAsync()"
What you are describing can be accomplished with the Task.WhenAll
method. Notice how the code turns into a simple one-liner. What happens is that each individual url begins downloading and then WhenAll
is used combine those operations into a single Task
which can be awaited.
Task<IEnumerable<string>> DownLoadAllUrls(string[] urls)
{
return Task.WhenAll(from url in urls select DownloadHtmlAsync(url));
}
tl;dr Iterators as implemented with yield are a blocking construct, so as of right now await and yield are incompatible.
Long Because iterating over an IEnumerable
is a blocking operation, calling a method marked as async
will still execute it in a blocking manner, since it has to wait for that operation to finish.
async Task<IEnumerable<Foo>> Method(String [] Strs)
{
foreach(var str in strs)
{
yield return await DoSomethingAsync( str)
}
}
The awaiting Method
mixes meanings. Do you want to wait until the Task
has an IEnumerable
and then block on iterating over it? Or are you trying to await each value of the IEnumerable
?
I assume the second is the desired behavior and in that case the existing Iterator semantics will not work. The IEnumerator<T>
interface is basically
public interface IEnumerator<T>
T Current;
bool MoveNext();
}
I'm ignoring Reset()
since it makes no sense for a sequence of asynchronous results. But what you would need is something like this:
public interface IAsyncEnumerator<T>
T Current;
Task<bool> MoveNext();
}
Of course, foreach
also won't work with this and you'd have to iterate manually like this:
var moveNext = await asyncEnumerator.MoveNext();
while(moveNext) {
// get the value that was fetche asynchronously
var v = asyncEnumerator.Current;
// do something with that value
// suspend current execution context until next value arrives or we are done
moveNext = await asyncEnumerator.MoveNext();
}