I working with AsyncEnumerables to read a bunch of files into my C# application (.NET 6) and want to add an option to cancel reading. However I'm confused by the behaviour of ToArrayAsync
when provided with a cancellationToken
.
I tried to break it down to this snippet:
var enumerable = Enumerable.Range(1, 10)
.ToAsyncEnumerable()
.SelectAwait(async x => { await Task.Delay(1000); return x; });
var cts = new CancellationTokenSource();
var resultTask = enumerable.ToArrayAsync(cts.Token);
cts.Cancel();
var z = await resultTask;
From my understanding SelectAwait
and ToArrayAsync
should evaluate one entry after another (which is supported by timing it). I'd expect ToArrayAsync
to check the provided token after everyone of those steps. I tried looking up the source for it and think it boils down to this bit, seemingly also supporting my assumption:
public ValueTask<bool> MoveNextAsync()
{
_cancellationToken.ThrowIfCancellationRequested();
return _source.MoveNextAsync(); // REVIEW: Signal cancellation through task or synchronously?
}
However in practice ToArrayAsync
fully evaluates all entries of the enumerable (taking the full 10 seconds) and returns without a cancellation-exception. Calling ToArrayAsync
with an already cancelled token immediately throws, so presumably it only checks at the very start of its execution?
I know I can work around that by using SelectAwaitWithCancellation
and checking to token inside its lambda, but I'm unsure about my understanding of ToArrayAsync
and AsyncEnumerables in general, so I'd be happy about clarification about how it works and what's the design thinking behind it.