Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return IAsyncEnumerable from an async method

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:

  1. Iterating over the IAsyncEnumerable and yielding the result immediately back.
await foreach(var item in Bar())
{
    yield return item;
}
  1. Creating a struct which can store an 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?

like image 389
Twenty Avatar asked Jan 10 '20 21:01

Twenty


People also ask

How do I return from async method?

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.

Can async return value?

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.

Can async return void?

Event handlers naturally return void, so async methods return void so that you can have an asynchronous event handler.

Is yield return async?

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.


1 Answers

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.

like image 112
Stephen Cleary Avatar answered Sep 18 '22 14:09

Stephen Cleary