我使用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
,该方法才会恢复执行。GetSequence
和await foreach
不是并行运行的,它们轮流在同一个异步任务中执行更多的代码。
如果DoSomethingAsync抛出异常
则await e.MoveNextAsync()
将重新抛出异常。然后枚举数将被销毁。然后,异常将沿着堆栈流向下一个异常处理程序。
您不需要使用取消令牌来阻止GetSequence
恢复执行。实际上,中断await foreach
循环也会中止执行,就好像yield return
从未恢复一样。导致GetSequence
方法中的任何finally{}
块执行,如果您有任何。