IAsyncEnumerable<T> 和迭代器生成的 IEnumerable<Task> 有什么区别<T>?



我正在努力找出IAsyncEnumerable<T>带来的优势,而不是IEnumerable<Task<T>>

我写了下面的类,它允许我等待一个数列,每个数列之间有定义的延迟:

class DelayedSequence : IAsyncEnumerable<int>, IEnumerable<Task<int>> {
readonly int _numDelays;
readonly TimeSpan _interDelayTime;
public DelayedSequence(int numDelays, TimeSpan interDelayTime) {
_numDelays = numDelays;
_interDelayTime = interDelayTime;
}
public IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken cancellationToken = default) {
async IAsyncEnumerable<int> ConstructEnumerable() {
for (var i = 0; i < _numDelays; ++i) {
await Task.Delay(_interDelayTime, cancellationToken);
yield return i;
}
}
return ConstructEnumerable().GetAsyncEnumerator();
}
public IEnumerator<Task<int>> GetEnumerator() {
IEnumerable<Task<int>> ConstructEnumerable() {
for (var i = 0; i < _numDelays; ++i) {
yield return Task.Delay(_interDelayTime).ContinueWith(_ => i);
}
}
return ConstructEnumerable().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

这个类实现了IAsyncEnumerable<int>IEnumerable<Task<int>>。我可以使用await foreachforeach对其进行迭代,并得到相同的结果:

var delayedSequence = new DelayedSequence(5, TimeSpan.FromSeconds(1d));
await foreach (var i in delayedSequence) {
Console.WriteLine(i);
}
foreach (var t in delayedSequence) {
Console.WriteLine(await t);
}

两次迭代都显示数字0到4,每行之间有一秒的延迟。

唯一的优势是与取消能力(即传入的cancellationToken)有关吗?还是有什么我没看到的场景?

等待一个序列,每个序列之间有一个定义的延迟

延迟发生在不同的时间。IEnumerable<Task<T>>立即返回下一个元素,然后是awaitIAsyncEnumerable<T>await是下一个元素。

IEnumerable<Task<T>>是一个(同步)枚举,其中每个元素都是异步的。当您有已知数量的操作要执行,然后每个项目异步独立到达时,这是正确的类型。

例如,此类型通常用于同时发送多个REST请求。要发出的REST请求的数量在开始时是已知的,并且每个请求都被Select转换为异步REST调用。生成的可枚举对象(或集合)通常会传递给await Task.WhenAll,以异步等待它们全部完成。

IAsyncEnumerable<T>是异步枚举;也就是说,它的MoveNext实际上是一个异步MoveNextAsync。当您有未知数量的项目要迭代,并且获得下一个(或下一个批处理)(可能)是异步的时,这是正确的类型。

例如,这种类型通常用于API的分页结果。在本例中,您不知道最终将返回多少元素。事实上,在检索下一页的结果之前,您甚至可能不知道自己是否已经到达终点。因此,即使确定是否有下一项也是异步操作。

最新更新