Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Proposal] async support for yield keyword #1410

Closed
sgjsakura opened this issue Mar 20, 2015 · 11 comments
Closed

[Proposal] async support for yield keyword #1410

sgjsakura opened this issue Mar 20, 2015 · 11 comments
Assignees
Labels
Area-Language Design Resolution-Duplicate The described behavior is tracked in another issue Verified

Comments

@sgjsakura
Copy link

Hi all., this requirement is triggered when I try to implement a general SelectAsync extension method for IEnumerable<T> type for an async selector in current version of C#. I wish the source code to be

public static async Task<IEnumerable<TResult>> SelectAsync<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, Task<TResult>> selector)
{
  foreach (var i in source)
  {
    yield return await selector(i);
  }
}

But the compiler does not accept the above code and says yield is not invalid in this context because the return type of this method is not IEnumerable<T> type.

The above code can also make a little change and thus will be another form: If we remove the async and await keyword, the return type is now IEnumerable<Task<T>>, and the next step for us should be convert IEnumerable<Task<T>> into Task<IEnumerable<T>>, this is just the proposal decribled in #261.

The async method pattern and iteration pattern are both great in C#, and I'm strongly wishing a greater combination for them.

Thank you~~

@KalitaAlexey
Copy link

Your code is incorrect. Task<IEnumerable> is awaitable task (which can be awaited once) and then results can be retrieved from this task.
But body of your method yields IEnumerable<Task>

@paulomorgado
Copy link

Are you proposing a Task<IEnumerable<TResult>> or an IEnumerable<Task<TResult>>?

@HaloFour
Copy link

There is already a proposal to accomplish this: #261.

But the result wouldn't be Task<IEnumerable<T>>, it would likely be something like IAsyncEnumerable<T> where you can await on each element in the stream.

@sgjsakura
Copy link
Author

@paulomorgado Actually the problem is yield keyword cannot be used in a method with async Task<IEnumerable<T>> return type.

Well, if my problem can be solved then IEnumerable<Task<TResult>> can be easily converted to Task<IEnumerable<TResult>> using the code as:

public static async Task<IEnumerable<TResult>> Unwrap<TSource, TResult>(this IEnumerable<Task<T>> source)
{
  foreach (var i in source)
  {
    yield return await i;
  }
}

Thank you for your kind comment :-)

@sgjsakura
Copy link
Author

@HaloFour Just as I mentioned in this issue, there's some relationship between this issue and the issue #261.

Thank you for your kind comment :-)

@sgjsakura
Copy link
Author

@KalitaAlexey It may be difficult to define the clear concept for yielding in an async method, and it may have to take serious adjustment to compiler code generation for this issue.

@HaloFour
Copy link

@sgjsakura

Indeed, but Task<IEnumerable<T>> makes no sense for this since Task<T> can only ever return a single value. You'd be waiting on the full materialization of the iterator. Also, async functions don't imply any threading so the iterator would execute synchronously anyway. This is why they're looking at alternative return values like IAsyncEnumerable<T> where you can await on each element separately.

Also note that you can accomplish all of this today with the Microsoft Rx Framework, which supports LINQ operations over IObservable<T> sequences (a push-model version of IAsyncEnumerable<T> and IEnumerable<T>). They even have a decent helper method for writing an asynchronous version of an iterator method which doesn't require much more code than a normal iterator method.

@gafter
Copy link
Member

gafter commented Mar 20, 2015

I am tempted to close this as a dup of #261, as that is intended to support the scenarios.

@gafter gafter self-assigned this Mar 20, 2015
@gafter gafter added this to the 1.0 (stable) milestone Mar 20, 2015
@gafter gafter assigned MadsTorgersen and unassigned gafter Mar 25, 2015
@gafter gafter modified the milestones: C# 7 and VB 15, 1.0 (stable) Mar 25, 2015
@gafter
Copy link
Member

gafter commented Mar 25, 2015

Dup of #261

@gafter gafter closed this as completed Mar 25, 2015
@gafter gafter added Resolution-Duplicate The described behavior is tracked in another issue Verified labels Mar 25, 2015
@gafter gafter assigned gafter and unassigned MadsTorgersen Mar 25, 2015
@gafter gafter modified the milestone: C# 7 and VB 15 Nov 26, 2015
@uxsoft
Copy link

uxsoft commented Feb 10, 2016

I would like to second the Rx-IObservable idea. I think this makes the best sense.

Like this:

public async IObservable GetItemsAsync()
{
string item1 = await GetSomethingAsync();
yield return item1;

string item2 = await GetSomethingElseAsync();
yield return item2;

}

SOURCE: https://www.interact-sw.co.uk/iangblog/2013/11/29/async-yield-return

@kind-serge
Copy link

I would recommend to use the AsyncEnumerator library. Example:

    using System.Collections.Async;
    
    IAsyncEnumerable<Bar> ConvertGoodFoosToBars(IAsyncEnumerable<Foo> items)
    {
        return items
          .Where(foo => foo.IsGood)
          .Select(foo => Bar.FromFoo(foo));
    }

You can find more on GitHub page, and use it via NuGet package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Language Design Resolution-Duplicate The described behavior is tracked in another issue Verified
Projects
None yet
Development

No branches or pull requests

8 participants