迭代时如何突破IAsyncEnumerable?

时间:2018-12-23 18:25:41

标签: c# c#-8.0 iasyncenumerable

C#8添加了对异步迭代器块的支持,因此您可以等待事件并返回IAsyncEnumarator而不是IEnumerable

public async IAsyncEnumerable<int> EnumerateAsync() {
    for (int i = 0; i < 10; i++) {
        yield return i;
        await Task.Delay(1000);
    }
}

具有如下所示的非阻塞使用代码:

await foreach (var item in EnumerateAsync()) {
    Console.WriteLine(item);
}

这将导致我的代码运行大约10秒钟。但是,有时候我想在所有元素都被消耗之前突围await foreach。但是,对于break,我们需要等到当前等待的Task.Delay完成。 我们如何在不等待任何悬空的异步任务的情况下立即跳出循环?

1 个答案:

答案 0 :(得分:3)

使用CancellationToken是解决方案,因为这是唯一可以取消代码中Task.Delay的东西。在IAsyncEnumerable中获取它的方法是在创建它时将其作为参数传递,所以让我们这样做:

public async IAsyncEnumerable<int> EnumerateAsync(CancellationToken cancellationToken = default) {
    for (int i = 0; i < 10; i++) {
        yield return i;
        await Task.Delay(1000, cancellationToken);
    }
}

在消费方面:

// In this example the cancellation token will be caneled after 2.5 seconds
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2.5));
await foreach (var item in EnumerateAsync(cts.Token)) {
    Console.WriteLine(item);
}

当然,这将在返回3个元素后取消枚举,但将以TaskCanceledException中抛出的Task.Delay结尾。要正常退出await foreach,我们必须抓住它并在生产方中断:

public async IAsyncEnumerable<int> EnumerateAsync(CancellationToken cancellationToken = default) {
    for (int i = 0; i < 10; i++) {
        yield return i;
        try {
            await Task.Delay(1000, cancellationToken);
        } catch (TaskCanceledException) {
            yield break;
        }
    }
}

注意

截至目前,该功能仍处于预览状态,可能会发生更改。如果您对此主题感兴趣,可以在CancellationTokenwatch a discussion of the C# language teamIAsyncEnumeration