Async-streams are asynchronous variants of enumerables, where getting the next element may involve an asynchronous operation. They are types that implement `IAsyncEnumerable<T>`.
```C#
```csharp
// Those interfaces will ship as part of .NET Core 3
namespaceSystem.Collections.Generic
{
...
...
@@ -40,7 +40,7 @@ An async-iterator method is a method that:
3. uses both `await` syntax (`await` expression, `await foreach` or `await using` statements) and `yield` statements (`yield return`, `yield break`).
For example:
```C#
```csharp
asyncIAsyncEnumerable<int>GetValuesFromServer()
{
while(true)
...
...
@@ -86,7 +86,7 @@ thereby allowing consumers of async-streams to control cancellation.
A producer of async-streams can make use of the cancellation token by writing an
`IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken)` async-iterator method in a custom type.
```C#
```csharp
Ee=((C)(x)).GetAsyncEnumerator(default);
try
{
...
...
@@ -117,7 +117,8 @@ But it contains additional state:
- a promise of a value-or-end,
- a current yielded value of type `T`,
- an `int` capturing the id of the thread that created it,
- a `bool` flag indicating "dispose mode".
- a `bool` flag indicating "dispose mode",
- a `CancellationTokenSource` for combining tokens (in enumerables).
The central method of the state machine is `MoveNext()`. It gets run by `MoveNextAsync()`, or as a background continuation initiated from these from an `await` in the method.
...
...
@@ -133,8 +134,8 @@ Compared to the state machine for a regular async method, the `MoveNext()` for a
- to support handling a `yield return` statement, which saves the current value and fulfills the promise with result `true`,
- to support handling a `yield break` statement, which sets the dispose mode on and jumps to the enclosing `finally` or exit,
- to dispatch execution to `finally` blocks (when disposing),
- to exit the method, which fulfills the promise with result `false`,
- to catch exceptions, which set the exception into the promise.
- to exit the method, which disposes the `CancellationTokenSource` (if any) and fulfills the promise with result `false`,
- to catch exceptions, which disposes the `CancellationTokenSource` (if any) and sets the exception into the promise.
(The handling of an `await` is unchanged)
This is reflected in the implementation, which extends the lowering machinery for async methods to:
...
...
@@ -143,7 +144,7 @@ This is reflected in the implementation, which extends the lowering machinery fo
3. produce additional state and logic for the promise itself (see `AsyncIteratorRewriter`, which produces various other members: `MoveNextAsync`, `Current`, `DisposeAsync`,
and some members supporting the resettable `ValueTask` behavior, namely `GetResult`, `SetStatus`, `OnCompleted`).
The kick-off method and the initialization of the state machine for an async-iterator method follows those for regular iterator methods.
In particular, the synthesized `GetAsyncEnumerator()` method is like `GetEnumerator()` except that it sets the initial state to to StateMachineStates.NotStartedStateMachine (-1):
/* copy each parameter proxy, or copy the token parameter if it's not default for any parameter marked with [EnumeratorCancellation] attribute */
/* copy each parameter proxy, or in the case of the parameter marked with [EnumeratorCancellation] combine it with `GetAsyncEnumerator`'s `token` parameter */
}
```
For the parameter with `[EnumeratorCancellation]`, `GetAsyncEnumerator` initializes it by combining the two available tokens:
<value>Async-iterator member has one or more parameters of type 'CancellationToken' but none of them is decorated with the 'EnumeratorCancellation' attribute, so the cancellation token parameter from the generated 'IAsyncEnumerable<>.GetAsyncEnumerator' will be unconsumed</value>
<value>Method '{0}' specifies a 'class' constraint for type parameter '{1}', but corresponding type parameter '{2}' of overridden or explicitly implemented method '{3}' is not a reference type.</value>
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// builder.SetException(ex); OR _promiseOfValueOrEnd.SetException(ex); /* for async-iterator method */
// builder.SetException(ex); OR if (this.combinedTokens != null) this.combinedTokens.Dispose(); _promiseOfValueOrEnd.SetException(ex); /* for async-iterator method */
@@ -18,6 +19,7 @@ private sealed class AsyncIteratorRewriter : AsyncRewriter
privateFieldSymbol_promiseOfValueOrEndField;// this struct implements the IValueTaskSource logic
privateFieldSymbol_currentField;// stores the current/yielded value
privateFieldSymbol_disposeModeField;// whether the state machine is in dispose mode (ie. skipping all logic except that in `catch` and `finally`, yielding no new elements)
privateFieldSymbol_combinedTokensField;// CancellationTokenSource for combining tokens
// true if the iterator implements IAsyncEnumerable<T>,
(byte)SignatureTypeCode.TypeHandle,(byte)WellKnownType.ExtSentinel,(byte)(WellKnownType.System_Threading_CancellationTokenSource-WellKnownType.ExtSentinel),// Return Type
(byte)SignatureTypeCode.TypeHandle,(byte)WellKnownType.ExtSentinel,(byte)(WellKnownType.System_Threading_CancellationToken-WellKnownType.ExtSentinel),// Return Type