Take the following the methods:
public async IAsyncEnumerable<int> Foo()
{
await SomeAsyncMethod();
return Bar(); // Throws since you can not return values from iterators
}
public async IAsyncEnumerable<int> Bar()
{
for(int i = 0; i < 10; i++)
{
await Task.Delay(100);
yield return i;
}
}
I wonder what the best practice would be, to do, what the code above tries to. Basically returning an IAsyncEnumerable
from an async
method.
For myself I can imagine two ways:
IAsyncEnumerable
and yielding the result immediately back. await foreach(var item in Bar())
{
yield return item;
}
IAsyncEnumerable
temporarily, which seems to be the better solution, but still kind of overkill. return new AsyncEnumerableHolder<int>(Bar());
public struct AsyncEnumerableHolder<T>
{
public readonly IAsyncEnumerable<T> Enumerable;
public AsyncEnumerableHolder(IAsyncEnumerable<T> enumerable)
{
Enumerable = enumerable;
}
}
Is there any better way to achieve this behavior?
You use the void return type in asynchronous event handlers, which require a void return type. For methods other than event handlers that don't return a value, you should return a Task instead, because an async method that returns void can't be awaited.
Async functions enable us to write promise based code as if it were synchronous, but without blocking the execution thread. It operates asynchronously via the event-loop. Async functions will always return a value.
Event handlers naturally return void, so async methods return void so that you can have an asynchronous event handler.
Using an async yield return statement requires that the method be asynchronous, making use of async/await. Usually an async method will return a task. Your first thought when using yield return in your async method may be to have the method return Task of IEnumerable.
The struct approach wouldn't work. If you want to asynchronously return an IAsyncEnumerator<T>
value, you could use Task<IAsyncEnumerator<T>>
with return Bar();
. However, that would be unusual. It would be much more natural to create a new IAsyncEnumerator<T>
that incorporates await SomeAsyncMethod()
at the beginning of the asynchronous enumerable. To do this, you should use await
and yield
as suggested by your option (1):
public async IAsyncEnumerable<int> Foo()
{
await SomeAsyncMethod();
await foreach (var item in Bar())
yield return item;
}
On a side note, JavaScript has a very nice yield*
syntax for this kind of "enumerate this whole sequence into my result sequence" concept, and it supports both synchronous and asynchronous sequences. C# does not have this kind of syntax for either synchronous or asynchronous sequences.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With