0

我正在尝试找出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)有关吗?还是有一些我在这里看不到的场景?

4

1 回答 1

6

等待一系列数字,每个数字之间有定义的延迟

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

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

例如,这种类型通常在同时发送多个 REST 请求时使用。一开始就知道要发出的 REST 请求的数量,并且每个请求都被Select编入一个异步 REST 调用。然后通常将生成的可枚举(或集合)传递await Task.WhenAll给以异步等待它们全部完成。

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

例如,这种类型通常在从 API 分页结果时使用。在这种情况下,您不知道最终将返回多少个元素。事实上,在检索到下一页结果之前,您甚至可能都不知道自己是否已经结束。所以即使判断是否有一项也是异步操作。

于 2021-01-21T23:13:40.373 回答