Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use yield in async C# task

I am trying to you use yield and return a result from converting X into Y in an async task. But, I am getting an error on select. The error is:

Error CS1942 The type of the expression in the select clause is incorrect. Type inference failed in the call to 'Select'.

public async Task<Result<dynamic>> GetYAsync(IEnumerable<X> infos)
    {
        return Task.WhenAll(from info in infos.ToArray() select async ()=>
        {
            yield return await new Y(info.Id, "Start");
        });
    }
like image 719
Aydin Homay Avatar asked Aug 15 '18 06:08

Aydin Homay


People also ask

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.

What does yield do in C?

The yield keyword performs custom and stateful iteration and returns each element of a collection one at a time sans the need of creating temporary collections. The yield keyword, first introduced in C# 2.0, T returns an object that implements the IEnumerable interface.

Can async method have return value?

Async methods can have the following return types: Task, for an async method that performs an operation but returns no value. Task<TResult>, for an async method that returns a value. void , for an event handler.

How do you use yield return?

To use "yield return", you just need to create a method with a return type that is an IEnumerable (arrays and collections in. Net implements IEnumerable interface) with a loop and use "yield return" to return a value to set in the loop body.


2 Answers

Short answer: you can't use an asynchronous yield statement.

But in most cases, you don't need to. Using LINQ you can aggregate all tasks before passing them into Task.WaitAll. I simplified your example to return an IEnumerable<int>, but this will work with every type.

public class Program
{
   public static Task<int> X(int x) 
   {
       return Task.FromResult(x);
   }
    
   public static async Task<IEnumerable<int>> GetYAsync(IEnumerable<int> infos)
   {
       var res = await Task.WhenAll(infos.Select(info => X(info)));
       return res;
   }
    
   public static async void Main()
   {
       var test = await GetYAsync(new [] {1, 2, 3});
       Console.WriteLine(test);
   }
}

Your example has another error await new Y(...), a constructor cannot be asynchronous, therefore I replaced it with an asynchronous function. (As hinted in the comments, it is technically possible to create a custom awaitable type and create this type with new, although this is rarely used),

The example above uses infos.Select to create a list of pending tasks, returned by invoking the function X. This list of tasks will then be awaited and returned.

This workaround should fit most cases. Real asynchronous iterators, as for example in JavaScript, are not supported in .Net.

Update: This feature is currently suggested as a language proposal: Async Streams. So maybe we will see this in the future.

Update: If you need asynchronous iterators, there are a few options currently available:

  1. Reactive Stream, RX.Net, which provides you with asynchronous observable streams based on events.
  2. There are implementations of asynchronous iterators or asynchronous enumerables AsyncEnumerable or .Net Async Enumerable
like image 154
Iqon Avatar answered Sep 22 '22 03:09

Iqon


You do not. Async Enum support (and yield is there to implement enumerable) comes with C# 8 some point in 2019 as it looks. So, for now the answer is simply that you do not.

The reason you get the error is that you can also not returna Result. Yield (return) is specific to implementing enumerations. Your method signature does not match.

like image 27
TomTom Avatar answered Sep 20 '22 03:09

TomTom