IAsyncEnumerable<T> 和迭代器生成的 IEnumerable<Task<T>> 之间有什么区别?
What's the difference between IAsyncEnumerable<T> and an iterator-generated IEnumerable<Task<T>>?
我正在尝试找出 IAsyncEnumerable<T>
带来的优势,例如 IEnumerable<Task<T>>
。
我写了以下 class 允许我等待一系列数字,每个数字之间有定义的延迟:
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();
}
此 class 实现了 IAsyncEnumerable<int>
和 IEnumerable<Task<int>>
。我可以使用 await foreach
和 foreach
对其进行迭代并得到相同的结果:
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
)?还是有一些我在这里没有看到的场景?
wait for a sequence of numbers with a defined delay between each one
延迟发生在不同的时间。 IEnumerable<Task<T>>
立即 returns 下一个元素,然后 await
ed。 IAsyncEnumerable<T>
await
是下一个元素。
IEnumerable<Task<T>>
是一个(同步)枚举,其中每个元素都是异步的。当您要执行的操作数量已知并且每个项目独立异步到达时,这是适合使用的类型。
例如,这种类型通常用于同时发送多个 REST 请求。要发出的 REST 请求的数量在一开始就已知,每个请求都 Select
编入一个异步 REST 调用中。然后通常将生成的可枚举(或集合)传递给 await Task.WhenAll
以异步等待它们全部完成。
IAsyncEnumerable<T>
是异步枚举;即 its MoveNext
is actually an asynchronous MoveNextAsync
。当您要迭代的项目数量未知并且获取下一个(或下一批)是(可能)异步时,这是使用的正确类型。
例如,这种类型通常用于 API 的分页结果。在这种情况下,您不知道最终会返回多少元素。事实上,在检索到下一页结果之前,您甚至可能不知道自己是否到了最后。所以连判断是否有是下一项也是异步操作
我正在尝试找出 IAsyncEnumerable<T>
带来的优势,例如 IEnumerable<Task<T>>
。
我写了以下 class 允许我等待一系列数字,每个数字之间有定义的延迟:
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();
}
此 class 实现了 IAsyncEnumerable<int>
和 IEnumerable<Task<int>>
。我可以使用 await foreach
和 foreach
对其进行迭代并得到相同的结果:
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
)?还是有一些我在这里没有看到的场景?
wait for a sequence of numbers with a defined delay between each one
延迟发生在不同的时间。 IEnumerable<Task<T>>
立即 returns 下一个元素,然后 await
ed。 IAsyncEnumerable<T>
await
是下一个元素。
IEnumerable<Task<T>>
是一个(同步)枚举,其中每个元素都是异步的。当您要执行的操作数量已知并且每个项目独立异步到达时,这是适合使用的类型。
例如,这种类型通常用于同时发送多个 REST 请求。要发出的 REST 请求的数量在一开始就已知,每个请求都 Select
编入一个异步 REST 调用中。然后通常将生成的可枚举(或集合)传递给 await Task.WhenAll
以异步等待它们全部完成。
IAsyncEnumerable<T>
是异步枚举;即 its MoveNext
is actually an asynchronous MoveNextAsync
。当您要迭代的项目数量未知并且获取下一个(或下一批)是(可能)异步时,这是使用的正确类型。
例如,这种类型通常用于 API 的分页结果。在这种情况下,您不知道最终会返回多少元素。事实上,在检索到下一页结果之前,您甚至可能不知道自己是否到了最后。所以连判断是否有是下一项也是异步操作