Skip to content

Instantly share code, notes, and snippets.

@wi7a1ian
Created October 7, 2020 09:31
Show Gist options
  • Save wi7a1ian/d0dcced7d47d3f88e6cd98a6539945e3 to your computer and use it in GitHub Desktop.
Save wi7a1ian/d0dcced7d47d3f88e6cd98a6539945e3 to your computer and use it in GitHub Desktop.
Explanation of `EnumeratorCancellation` in async enumerable #async #csharp
public class Program
{
// https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/november/csharp-iterating-with-async-enumerables-in-csharp-8
public static async Task Main(string[] args)
{
using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(300));
/*
* Passing the token directly to the method is easier, but it doesn’t work when you’re handed an arbitrary IAsyncEnumerable<T>
* from some other source but still want to be able to request cancellation of everything that composes it.
* In corner-cases, it can also be advantageous to pass the token to GetAsyncEnumerator, as doing so avoids “burning in” the token
* in the case where the single enumerable will be enumerated multiple times: By passing it to GetAsyncEnumerator,
* a different token can be passed each time. Of course, it’s also possible that two different tokens end up getting passed into the same iterator,
* one as an argument to the iterator and one via GetAsyncEnumerator. In that case, the compiler-generated code handles this
* by creating a new linked token that will have cancellation requested when either of the two tokens has cancellation requested,
* and that new “combined” token will be the one the iterator body sees.
*/
//await foreach (int item in RangeAsync(0, 5, cts.Token)) // same as..
await foreach (int item in RangeAsync(0, 5).WithCancellation(cts.Token)) // but different than...
//await foreach (int item in RangeAsync(0, 5, cts.Token).WithCancellation(cts.Token))
{
Console.Write(item + " ");
}
// the only way to cancel async enumerable when its provided by another method
await foreach (int item in RangeAsyncWrapper().WithCancellation(cts.Token))
{
Console.Write(item + " ");
}
}
private static async IAsyncEnumerable<int> RangeAsync(int start, int count,
[EnumeratorCancellation] CancellationToken ct = default)
{
for (int i = 0; i < count; i++)
{
await Task.Delay(100, ct);
yield return start + i;
}
}
private static IAsyncEnumerable<int> RangeAsyncWrapper()
=> RangeAsync(0, 5);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment