我正在使用Reactive扩展(版本2.1,以防万一)开始开发,对于我的示例应用程序,我需要按一定间隔推送一系列int值,即每1秒。
我知道,我可以使用Observable.Range<int>(0,10)
创建一个序列,但我无法弄清楚如何设置推送之间的相对时间。我已经尝试了Delay()
但是在开始时仅将序列移动了一次。
然后我找到了Observable.Generate()
方法,可以通过下一步方式调整到此任务:
var delayed = Observable.
Generate(0, i => i <= 10, i => i + 1, i => i,
i => TimeSpan.FromSeconds(1));
但这似乎只适用于简单的'for-each-like'定义序列。 所以,一般来说,我的问题是,我们是否可以获取任何源序列并用一些代理来包装它,这些代理将从源中提取消息并将其推迟进一步推迟?
S--d1--d2--d3--d4--d5-|
D--d1-delay-d2-delay-d3-delay-d4-delay-d5-|
P.S。如果此方法与ReactiveExtensions的概念相矛盾,请另请注意。我不想“无论如何”这样做,并且他们将来会遇到其他一些设计问题。
PPS 一般的想法是确保 输出序列在事件之间具有指定的间隔 ,尽管如果输入序列是有限的或无限的,以及它推动事件的频率。
答案 0 :(得分:8)
Observable.Interval是您想要查看的内容。它将生成一个基于0的长值,在您指定的每个时间间隔内递增1,例如:
Observable.Interval(TimeSpan.FromSeconds(1)).Subscribe(x => Console.WriteLine(x));
然后,您可以根据需要使用投影(Select
)来偏移/更改此值。
您还可以使用Zip运算符将一个流“调整”到另一个流 - 您可能也想查看它。 Zip将来自两个流的事件组合在一起,因此它以当前最慢的流的速度发出。 Zip也非常灵活,它可以压缩任意数量的流,甚至可以将IObservable压缩到IEnumerable。这是一个例子:
var pets = new List<string> { "Dog", "Cat", "Elephant" };
var pace = Observable.Interval(TimeSpan.FromSeconds(1))
.Zip(pets, (n, p) => p)
.Subscribe(x => Console.WriteLine(x), () => Console.WriteLine("Done"));
这会以1秒的间隔写出宠物。
根据P.P.S.如上所述,我将给出另一个答案 - 我将留下这个作为参考,因为它无论如何都是一种有用的技术。
答案 1 :(得分:2)
因此,为了澄清,您希望输出以不超过间隔的速率推送输入,但是否则尽可能快。
在这种情况下试试这个。 input
变量构造是一种创建短暂的偶发序列的一种愚蠢的方式,有时更快,有时比2秒的速度慢。请注意,秒表的输出将显示Rx使用的计时器机制中的小不准确性。
var input = Observable.Interval(TimeSpan.FromSeconds(1)).Take(4);
input = input.Concat(Observable.Interval(TimeSpan.FromSeconds(5)).Take(2));
var interval = TimeSpan.FromSeconds(2);
var paced = input.Select(i => Observable.Empty<long>()
.Delay(interval)
.StartWith(i)).Concat();
var stopwatch = new Stopwatch();
stopwatch.Start();
paced.Subscribe(
x => Console.WriteLine(x + " " + stopwatch.ElapsedMilliseconds),
() => Console.WriteLine("Done"));
此示例的工作原理是将输入中的每个刻度线投影到一个序列中,该序列在开始时将tick作为单个事件,但在所需的时间间隔内不会OnComplete。然后连接得到的流流。如果输出当前被“刷新”,这种方法可以确保立即发出新的滴答声,但是否则会相互缓冲。
您可以将其包含在扩展方法中,使其成为通用的。
答案 2 :(得分:0)
这是做你想做的最简单的方法:
var delayed =
source.Do(x => Thread.Sleep(1000));
它会增加第二个延迟,但它会在第一个项目之前执行此操作。你当然可以结束一些逻辑,不要在开始时加减。那不会太难。
这是一种可以预测全新趋势延迟的替代方案。
var delayed =
Observable.Create<int>(o =>
{
var els = new EventLoopScheduler();
return source
.ObserveOn(els)
.Do(x => els.Schedule(() => Thread.Sleep(1000)))
.Subscribe(o);
});
答案 3 :(得分:0)
您正在寻找的是Buffer
扩展方法。它的签名定义如下:
public static IObservable<IList<TSource>> Buffer<TSource>(
this IObservable<TSource> source,
TimeSpan timeSpan)
它将以一种批量生成值的方式转换源序列,频率为timeSpan
。
答案 4 :(得分:0)
我知道这是一个老问题,但我认为我有正确答案。
压缩一个Observable.Timer生成&#39; ticks&#39;即使源Observable没有产生任何东西。这意味着一旦源生成另一个项目,将使用已生成但尚未消耗的任何刻度。导致生产者以稳定的速度生产物品时会在物品之间增加延迟,但如果生产者有时需要更长的时间来生产物品,这会产生物品爆发。
为了避免这种情况,你需要在你的observable生成的每个项目之间生成一个只生成一个项目的计时器。您可以使用Observable.Switch执行此操作:
var subject = new Subject<Unit>();
var producer = subject.SelectMany(
_ =>
{
return new[]
{
Observable.Return(true),
Observable.Timer(TimeSpan.FromSeconds(2))
.Select(q => false)
};
})
.Switch();