c# - 迭代时如何跳出 IAsyncEnumerable?

标签 c# c#-8.0 iasyncenumerable

C# 8 添加了对异步迭代器 block 的支持,因此您可以等待并返回 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 完成。 我们如何才能在不等待任何悬空的异步任务的情况下立即跳出该循环?

最佳答案

使用 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 个元素后取消枚举,但将以 Task.Delay 抛出的 TaskCanceledException 结束。要优雅地退出 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;
        }
    }
}

注意事项

截至目前,这仍处于预览阶段,可能会发生变化。如果您对此主题感兴趣,可以watch a discussion of the C# language team关于 IAsyncEnumeration 中的 CancellationToken

关于c# - 迭代时如何跳出 IAsyncEnumerable?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53906097/

相关文章:

c# - 不可能两次运行 IAsyncEnumerable 的枚举?

c# - 如何使用 C# ASP.NET 创建聊天框和通知系统

c# - 使用 C# 在设计器中隐藏调整大小标记?

c# - 特征递归模式目前在 Vs Code 的预览版中

c# - 在 Visual Studio 中使用预览功能和预览语言

c# - 如何同步运行异步枚举器方法并将其存储为 IEnumerable?

c# - 转换字符串得到错误的值

c# - 卡住datagridview中的第一行和前两列

c# - 用于检测 C# 8/可为空引用类型的编译器指令

c# - 在 foreach 循环中等待的正确方法