{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/corefx/issues/32684","repository_url":"https://api.github.com/repos/dotnet/corefx","labels_url":"https://api.github.com/repos/dotnet/corefx/issues/32684/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/corefx/issues/32684/comments","events_url":"https://api.github.com/repos/dotnet/corefx/issues/32684/events","html_url":"https://github.com/dotnet/corefx/issues/32684","id":367616580,"node_id":"MDU6SXNzdWUzNjc2MTY1ODA=","number":32684,"title":"Proposal: IAsyncEnumerable.ConfigureAwait(bool continueOnCapturedContext)","user":{"login":"stephentoub","id":2642209,"node_id":"MDQ6VXNlcjI2NDIyMDk=","avatar_url":"https://avatars0.githubusercontent.com/u/2642209?v=4","gravatar_id":"","url":"https://api.github.com/users/stephentoub","html_url":"https://github.com/stephentoub","followers_url":"https://api.github.com/users/stephentoub/followers","following_url":"https://api.github.com/users/stephentoub/following{/other_user}","gists_url":"https://api.github.com/users/stephentoub/gists{/gist_id}","starred_url":"https://api.github.com/users/stephentoub/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stephentoub/subscriptions","organizations_url":"https://api.github.com/users/stephentoub/orgs","repos_url":"https://api.github.com/users/stephentoub/repos","events_url":"https://api.github.com/users/stephentoub/events{/privacy}","received_events_url":"https://api.github.com/users/stephentoub/received_events","type":"User","site_admin":false},"labels":[{"id":165998434,"node_id":"MDU6TGFiZWwxNjU5OTg0MzQ=","url":"https://api.github.com/repos/dotnet/corefx/labels/api-ready-for-review","name":"api-ready-for-review","color":"5319e7","default":false},{"id":174276033,"node_id":"MDU6TGFiZWwxNzQyNzYwMzM=","url":"https://api.github.com/repos/dotnet/corefx/labels/area-System.Runtime","name":"area-System.Runtime","color":"d4c5f9","default":false}],"state":"open","locked":false,"assignee":{"login":"stephentoub","id":2642209,"node_id":"MDQ6VXNlcjI2NDIyMDk=","avatar_url":"https://avatars0.githubusercontent.com/u/2642209?v=4","gravatar_id":"","url":"https://api.github.com/users/stephentoub","html_url":"https://github.com/stephentoub","followers_url":"https://api.github.com/users/stephentoub/followers","following_url":"https://api.github.com/users/stephentoub/following{/other_user}","gists_url":"https://api.github.com/users/stephentoub/gists{/gist_id}","starred_url":"https://api.github.com/users/stephentoub/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stephentoub/subscriptions","organizations_url":"https://api.github.com/users/stephentoub/orgs","repos_url":"https://api.github.com/users/stephentoub/repos","events_url":"https://api.github.com/users/stephentoub/events{/privacy}","received_events_url":"https://api.github.com/users/stephentoub/received_events","type":"User","site_admin":false},"assignees":[{"login":"stephentoub","id":2642209,"node_id":"MDQ6VXNlcjI2NDIyMDk=","avatar_url":"https://avatars0.githubusercontent.com/u/2642209?v=4","gravatar_id":"","url":"https://api.github.com/users/stephentoub","html_url":"https://github.com/stephentoub","followers_url":"https://api.github.com/users/stephentoub/followers","following_url":"https://api.github.com/users/stephentoub/following{/other_user}","gists_url":"https://api.github.com/users/stephentoub/gists{/gist_id}","starred_url":"https://api.github.com/users/stephentoub/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stephentoub/subscriptions","organizations_url":"https://api.github.com/users/stephentoub/orgs","repos_url":"https://api.github.com/users/stephentoub/repos","events_url":"https://api.github.com/users/stephentoub/events{/privacy}","received_events_url":"https://api.github.com/users/stephentoub/received_events","type":"User","site_admin":false}],"milestone":{"url":"https://api.github.com/repos/dotnet/corefx/milestones/21","html_url":"https://github.com/dotnet/corefx/milestone/21","labels_url":"https://api.github.com/repos/dotnet/corefx/milestones/21/labels","id":3242632,"node_id":"MDk6TWlsZXN0b25lMzI0MjYzMg==","number":21,"title":"3.0","description":".NET Core 3.0, next major release after 2.1. See [official release dates](https://github.com/dotnet/core/blob/master/roadmap.md) for details.\r\n\r\nWe will move important issues from Future to 3.0 as the priorities and dates for next release become more clear.","creator":{"login":"karelz","id":5776068,"node_id":"MDQ6VXNlcjU3NzYwNjg=","avatar_url":"https://avatars2.githubusercontent.com/u/5776068?v=4","gravatar_id":"","url":"https://api.github.com/users/karelz","html_url":"https://github.com/karelz","followers_url":"https://api.github.com/users/karelz/followers","following_url":"https://api.github.com/users/karelz/following{/other_user}","gists_url":"https://api.github.com/users/karelz/gists{/gist_id}","starred_url":"https://api.github.com/users/karelz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/karelz/subscriptions","organizations_url":"https://api.github.com/users/karelz/orgs","repos_url":"https://api.github.com/users/karelz/repos","events_url":"https://api.github.com/users/karelz/events{/privacy}","received_events_url":"https://api.github.com/users/karelz/received_events","type":"User","site_admin":false},"open_issues":300,"closed_issues":2463,"state":"open","created_at":"2018-04-04T16:40:10Z","updated_at":"2018-10-25T18:46:48Z","due_on":"2019-03-31T07:00:00Z","closed_at":null},"comments":3,"created_at":"2018-10-08T01:43:11Z","updated_at":"2018-10-25T20:34:29Z","closed_at":null,"author_association":"MEMBER","body":"Related to https://github.com/dotnet/corefx/issues/32640.\r\n\r\n**Background**\r\n\r\nWith the addition of `IAsyncEnumerable<T>`, developers will be able to write asynchronous foreach loops, e.g.\r\n```C#\r\nforeach await (T item in source)\r\n{\r\n    ProcessItem(item);\r\n}\r\n```\r\n\r\nThis loop expands by the compiler into the equivalent of:\r\n```C#\r\nIAsyncEnumerator<T> e = source.GetAsyncEnumerator();\r\ntry\r\n{\r\n    while (await e.MoveNextAsync())\r\n    {\r\n        T item = e.Current;\r\n        ProcessItem(item);\r\n    }\r\n}\r\nfinally\r\n{\r\n    await e.DisposeAsync();\r\n}\r\n```\r\n\r\nNote that as part of that expansion, the compiler introduced several `await`s not visible in the original source code (which is part of why a developer annotates the `foreach` as `foreach await`).  This introduces an issue, in that a developer now no longer has explicit syntactic control for that `await` operation, which means that there’s nowhere for a developer to append `ConfigureAwait(false)` if they want to prevent the `await` from grabbing the current context and scheduling the continuation back to it.  Our guidelines are that library code by default should use `ConfigureAwait(false)` on every `await`, so the lack of that here is problematic for library authors.\r\n\r\n**Proposal**\r\n\r\nAllow a developer to write:\r\n```C#\r\nforeach await (T item in source.ConfigureAwait(false))\r\n{\r\n    ProcessItem(item);\r\n}\r\n```\r\n\r\nThis would be achieved with an extension method as follows:\r\n```C#\r\nnamespace System.Collections.Generic\r\n{\r\n    public static class AsyncEnumerable\r\n    {\r\n        public static ConfiguredAsyncEnumerable<T> ConfigureAwait(this IAsyncEnumerable<T> source, bool continueOnCapturedContext);\r\n    }\r\n}\r\n\r\nnamespace System.Runtime.CompilerServices\r\n{\r\n    public struct ConfiguredAsyncEnumerable<T>\r\n    {\r\n        public Enumerator GetAsyncEnumerator();\r\n\r\n        public struct Enumerator\r\n        {\r\n            public ConfiguredValueTaskAwaitable<T> MoveNextAsync();\r\n            public T Current { get; }\r\n            public ConfiguredValueTaskAwaitable DisposeAsync();\r\n        }\r\n    }\r\n}\r\n```\r\n\r\nBecause `foreach await` is able to bind by pattern, this `ConfiguredAsyncEnumerable<T>` can be `foreach await`’d, and the previously shown expansion will now how each of the `await`s operating on a `ConfiguredValueTaskAwaitable<T>` or `ConfiguredValueTaskAwaitable` instead of a `ValueTask<T>` or `ValueTask`.\r\n\r\n**Open issues:**\r\n-\t**Do we want this?** `foreach await` is just syntactic sugar for something a developer could otherwise write.  However, it’s really useful syntactic sugar, and forcing a developer that wants to iterate through an async enumerable in a library to expand it out manually seems wrong.\r\n-\t**Extension method type name and namespace**.  Presumably `AsyncEnumerable` is a nice name for a LINQ implementation to use.  Do we want to claim it for this?  But presumably it should also live in the same namespace as `IAsyncEnumerable<T>`, so that it’s always available when using that interface."},"comment":{"url":"https://api.github.com/repos/dotnet/corefx/issues/comments/433196040","html_url":"https://github.com/dotnet/corefx/issues/32684#issuecomment-433196040","issue_url":"https://api.github.com/repos/dotnet/corefx/issues/32684","id":433196040,"node_id":"MDEyOklzc3VlQ29tbWVudDQzMzE5NjA0MA==","user":{"login":"stephentoub","id":2642209,"node_id":"MDQ6VXNlcjI2NDIyMDk=","avatar_url":"https://avatars0.githubusercontent.com/u/2642209?v=4","gravatar_id":"","url":"https://api.github.com/users/stephentoub","html_url":"https://github.com/stephentoub","followers_url":"https://api.github.com/users/stephentoub/followers","following_url":"https://api.github.com/users/stephentoub/following{/other_user}","gists_url":"https://api.github.com/users/stephentoub/gists{/gist_id}","starred_url":"https://api.github.com/users/stephentoub/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stephentoub/subscriptions","organizations_url":"https://api.github.com/users/stephentoub/orgs","repos_url":"https://api.github.com/users/stephentoub/repos","events_url":"https://api.github.com/users/stephentoub/events{/privacy}","received_events_url":"https://api.github.com/users/stephentoub/received_events","type":"User","site_admin":false},"created_at":"2018-10-25T20:34:29Z","updated_at":"2018-10-25T20:34:29Z","author_association":"MEMBER","body":"> As an alternative to the static ConfigureAwait() method (which I don't like as a concept), could you handle this the same way you are handling passing the cancellation token, by pushing it outside the scope of IAsyncEnumerable?\r\n\r\nI'm not entirely sure what you mean, as the ConfigureAwait is entirely outside the scope of the IAsyncEnumerable, more so than with CancellationToken.  ValueTask, which is returned by MoveNextAsync and DisposeAsync, supports ConfigureAwait already, so this extension method on IAsyncEnumerable is just a way to get the compiler's pattern-matching for await foreach to pass that ConfigureAwait call on to each ValueTask it awaits.  It's implemented entirely on top of everything else, without any hooks or internals access or any such things needed."}}
{"actor":{"display_login":"stephentoub"}}