如何随着时间动态地延迟可观察的时间

时间:2018-10-26 12:48:29

标签: c# system.reactive rx.net

我当前具有一项功能,用户Timer()立即触发可观察对象,然后每隔x毫秒触发一次。

HoldPayloads = Observable.Merge(
    EnumeratedSymbolKeys.Select(
        o => Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
            h => o.PreviewMouseLeftButtonDown += h,
            h => o.PreviewMouseLeftButtonDown -= h)
            .Select(_ => Observable.Timer(DateTimeOffset.Now, TimeSpan.FromMilliseconds(o.Frequency))
            .TakeUntil(Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
                h => o.PreviewMouseLeftButtonUp += h,
                h => o.PreviewMouseLeftButtonUp -= h)))
                .Switch()
                .Select(_ => o.Payload)));

我想要的是,单击按钮后立即出现可观察到的火,然后经过较长的初始延迟后,开始以更快的间隔或逐渐减小的间隔重复到一个极限,如下所示:

--x------x--x--x--x--x--x-->

--x------x----x---x--x-x-x->

我尝试将Delay()Scan()结合使用以生成指数级较低的值以延迟执行,但无法使其正常工作。我在正确的轨道上吗?还有更好的方法来做这样的事情吗?

使用Shlomo的答案修订的代码:

HoldPayloads = Observable.Merge(
EnumeratedSymbolKeys.Select(
    o => Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
        h => o.PreviewMouseLeftButtonDown += h,
        h => o.PreviewMouseLeftButtonDown -= h)
        .Select(_ => Observable.Generate(
            1,
            q => true,
            i => i+1,
            i => i,
            i => i==1
                ? TimeSpan.FromMilliseconds(0)
                : i > 10
                    ? TimeSpan.FromMilliseconds(50)
                    : TimeSpan.FromMilliseconds(500/i))
        .TakeUntil(Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
            h => o.PreviewMouseLeftButtonUp += h,
            h => o.PreviewMouseLeftButtonUp -= h)))
            .Switch()
            .Select(_ => o.Payload)));

最终修改了条件,使第一项立即生效。

2 个答案:

答案 0 :(得分:2)

<style> .block { width: 100%; height: 500px; /* Height will be dynamic. It's just to test when element is not visible on initial view */ background: yellow; } .sticking-block { width: 100%; height 30px; /* Height will be dynamic */ background: red; } .sticking-block.sticky { position: fixed; bottom: 0; width: 100%; z-index: 999; } </style> <div class="block">some content </div> <div class="block">some content </div> <div class="block">some content </div> <div class="block">some content </div> <div class="sticking-block">this block sticks to the bottom </div> <div class="block">some content </div> <div class="block">some content </div>是您的朋友(在this页面上有很好的讨论)。

对于越来越频繁的值,将Observable.Generate替换为以下内容:

.Select(_ => Observable.Timer(DateTimeOffset.Now, TimeSpan.FromMilliseconds(o.Frequency))

无论您的模式是什么,都将其装入最后一个参数。将Observable.Generate( 1, _ => true, i => i + 1, i => i, i => i > 10 ? TimeSpan.FromMilliseconds(50) : TimeSpan.FromMilliseconds(500 / i) ) 看作是一个响应式的for循环,您可以使用所有的拨号。


编辑

使用上面的代码,Generate中的第一项将被延迟。如果您想立即获取第一项,然后延迟第二项,则可以执行以下操作:

Generate

原来的Observable.Generate( 1, _ => true, i => i + 1, i => i, i => i > 10 ? TimeSpan.FromMilliseconds(50) : TimeSpan.FromMilliseconds(500 / i) ) .StartWith(0) 现在是.Select(_ => Observable.Timer(DateTimeOffset.Now, TimeSpan.FromMilliseconds(o.Frequency)

答案 1 :(得分:0)

我只是想添加一个答案,以使最终查询更具可读性。我没有以任何方式更改答案,也不想让这个答案被接受甚至投票否决。我只是想帮助增加一些关于如何理解查询的清晰度。

我刚刚做了一些替换重构,并将查询更改为:

HoldPayloads =
    EnumeratedSymbolKeys
        .Select(o =>
            MouseDowns(o)
                .Select(_ => Generate().TakeUntil(MouseUps(o)))
                .Switch()
                .Select(_ => o.Payload))
        .Merge();

对我来说,这更容易阅读,查询的意图也很清楚。

仅保留MouseDownsMouseUpsGenerate的定义。这些是:

IObservable<Unit> MouseDowns(EnumeratedSymbolKey o) =>
    Observable
        .FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
            h => o.PreviewMouseLeftButtonDown += h, h => o.PreviewMouseLeftButtonDown -= h)
        .Select(EncodingProvider => Unit.Default);

IObservable<Unit> MouseUps(EnumeratedSymbolKey o) =>
    Observable
        .FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
            h => o.PreviewMouseLeftButtonUp += h, h => o.PreviewMouseLeftButtonUp -= h)
        .Select(EncodingProvider => Unit.Default);          

IObservable<int> Generate() =>
    Observable
        .Generate(1, i => true, i => i + 1, i => i,
            i => i == 1
                ? TimeSpan.FromMilliseconds(0) 
                : (i > 10 ? TimeSpan.FromMilliseconds(50) : TimeSpan.FromMilliseconds(500 / i)));

现在将它们分离开了,可以更容易地确认每一个都是正确的,并希望整个代码都是正确的。