Replies: 16 comments
-
Still think await is in the wrong place foreach (var item in seq) // blocking iteration of a regular sequence
async foreach (var item in seq) // non-blocking iteration of a regular sequence
foreach (await var item in asyncSeq) // blocking iteration of an async sequence
async foreach (await var item in asyncSeq) // non-blocking iteration of an async sequence So for the last two it should be foreach (var item await in asyncSeq) // blocking iteration of an async sequence
async foreach (var item await in asyncSeq) // non-blocking iteration of an async sequence With async foreach (var item await in asyncSeq) being task returning var task = async foreach (var item await in asyncSeq) and foreach (var item await in asyncSeq) being semantic sugar for await async foreach (var item await in asyncSeq) Await after the in should also be allowed (as it is currently) for the async return of the enumerable so; you could do something a bit yukky like: var task = async foreach (var item await in await asyncSeq) |
Beta Was this translation helpful? Give feedback.
-
That's not part of this proposal, I've borrowed it from a design note, you may discuss it in #43. |
Beta Was this translation helpful? Give feedback.
-
I can't help but think that this could be accomplished through a library function. Something like: public static Task ThenEach<T>(this IAsyncEnumerable<T> stream, Func<T, Task> action) { ... } I don't doubt that there would be some challenges in implementation, particularly in keeping it "single threaded". But nothing insurmountable. Of course that wouldn't work with arbitrary task-alikes. |
Beta Was this translation helpful? Give feedback.
-
Arbitrary tasklikes will be solved with |
Beta Was this translation helpful? Give feedback.
-
@jnm2, @HaloFour this killer feature here, at least for me, is |
Beta Was this translation helpful? Give feedback.
-
Note that |
Beta Was this translation helpful? Give feedback.
-
@alrz thanks for clarifying. async IAsyncEnumerable<HttpResponseMessage> DownloadAsync(IEnumerable<string> urls)
{
foreach(var url in urls)
{
yield return await new HttpClient().GetAsync(url);
}
} and that an intent of the async loops proposed here is to enable async IObservable<HttpResponseMessage> DownloadAsync(IEnumerable<string> urls)
{
async foreach(var url in urls)
{
yield return await new HttpClient().GetAsync(url);
}
} |
Beta Was this translation helpful? Give feedback.
-
foreach is a loop. |
Beta Was this translation helpful? Give feedback.
-
If iterations of a loop occur in parallel, is it still a loop? |
Beta Was this translation helpful? Give feedback.
-
this is not a parallel loop, just that it uses await as a break point to switch between iterations. |
Beta Was this translation helpful? Give feedback.
-
Thinking about this further I'm not sure I buy that argument. The |
Beta Was this translation helpful? Give feedback.
-
That's a valid concern. As an alternative to caching, I think we can do a non-blocking iteration but wait for |
Beta Was this translation helpful? Give feedback.
-
@HaloFour I shall defer to your own comments on that,
|
Beta Was this translation helpful? Give feedback.
-
@alrz |
Beta Was this translation helpful? Give feedback.
-
Indeed, I'm a bit on the fence about it. I believe that the functionality itself would be quite useful, the question is whether or not it's something that can be done reasonably via a library vs. a language feature. The tricky part is definitely trying to keep the coroutine handling single-threaded, which could probably be done by hijacking/wrapping the synchronization context. Otherwise a library couldn't really handle the continutation of the lambda action argument. |
Beta Was this translation helpful? Give feedback.
-
Relates to https://github.com/stephentoub/csharplang/blob/master/proposals/async-streams.md |
Beta Was this translation helpful? Give feedback.
-
Moved from dotnet/roslyn#8014
async loops
Currently
await
in loops, blocks the iteration until the awaitable returns.async
on a loop is to indicate that it may continue the iteration after anawait
is reached.This behaves like a list of running tasks and a
Task.WhenAll
but it provides language support for processing tasks as they complete.Note that in each iteration, we don't move to the next item until we get to an
await
, for the sake of fairness, we will do so even if the task has already been completed.No iteration would run in parallel but might be in waiting state at the same time — a completed task might be waiting for another iteration to get to an await or end of the loop block, that way we won't need any
lock
in the body of the loop.Similar to
async
on methods which would be redundant without anyawait
in the method body,async
on a loop have no effect without anyawait
in the loop body, so in that case a warning would be useful.An async loop may be used with
yield return
, e.g.I've used an
IObservable<T>
because if we want to return anIAsyncEnumerable<T>
we will need to cache the results as they complete so it would be preferable to use a blocking loop in that case.The
async
modifier can be added regardless of the source collection being anIAsyncEnumerable<T>
.For example, imagine we are iterating an async stream of integers, (example by @HaloFour)
The possible output might be the following since the multiple calls to
LookupUserAsync
could be dispatched concurrently:Whereas with an blocking foreach it would be the following since the async method would have to complete the loop body before iterating to the next element in the sequence:
When we are waiting for
MoveNextAsync
we won't go further and if any other tasks from previous iterations have been completed we will continue the loop body. So in case ofIAsycEnumerable<T>
it'll be possible for a "Found user" message to be printed prior to a "Looking up user" message.Other loops can be made
async
as well since this doesn't depend on iterating a particular collection,It might be preferable to make a fresh variable in each iteration for
async for
just likeforeach
.Beta Was this translation helpful? Give feedback.
All reactions