当异常发生时如何取消IAsyncEnumerable ?



我使用IAsyncEnumerable获取大量数据-像这样:

static async IAsyncEnumerable<int> GetSequence([EnumeratorCancellation] CancellationToken ct = default)
{
for (int i = 1; i <= 10; i++)
{
var data = await DoSomethingAsync()    // get data from web request
yield return data;
}
}

现在我的问题是:如果DoSomethingAsync抛出异常(可能是网络问题),我想取消剩余的请求,这是可能的吗?

我正在尝试下面的代码处理,但仍然需要帮助:

CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
try
{
IAsyncEnumerable<int> dataCol = GetSequence(token);
await foreach (var d in dataCol)
{
// do ...
}
}
catch (Exception)
{
source.Cancel(true);
throw;
}

欣赏别人的最佳实践

来自原语言提案

await foreach (var d in dataCol)
{
// do ...
}

等价于;

{
E e = dataCol.GetAsyncEnumerator();
try {
while (await e.MoveNextAsync()) {
var d = e.Current;
// do ...
}
}
finally {
... // Dispose e
}
}

调用MoveNextAsync会导致GetSequence方法执行到下一个yield return,或者直到抛出未处理的异常。直到下次调用MoveNextAsync,该方法才会恢复执行。GetSequenceawait foreach不是并行运行的,它们轮流在同一个异步任务中执行更多的代码。

如果DoSomethingAsync抛出异常

await e.MoveNextAsync()将重新抛出异常。然后枚举数将被销毁。然后,异常将沿着堆栈流向下一个异常处理程序。

您不需要使用取消令牌来阻止GetSequence恢复执行。实际上,中断await foreach循环也会中止执行,就好像yield return从未恢复一样。导致GetSequence方法中的任何finally{}块执行,如果您有任何。

最新更新