事件节流/排队-响应式扩展?

8
我希望在我的一个视图模型中实现一些限流行为。这是一个Silverlight应用程序,但我认为这并不特别重要。
考虑一个具有三个属性的类:
- Property1 - Property2 - Property3
每当更新其中一个属性时,都需要进行刷新。
private void Refresh()
{
    //Call out to the server, do something when it comes back
}

我的目标如下:
  • 如果正在进行刷新操作,我们最好取消向服务器的调用,并发起一个新请求
  • 如果更改属性,我们应该留出一小段时间(可能是0.1秒)等待其他更改。这样,如果多个属性被快速更改(例如,以编程方式),我们不会向服务器发送大量请求。这个0.1秒窗口在每次更改时可以重置,但不是必须的。

如果有影响的话,我正在使用ChannelFactory实现来调用服务器。

我可以使用什么样的模式来实现这个目标?这是反应式扩展能帮助我的吗?

编辑:

将Paul的答案标记为正确。虽然ReactiveUI目前不能用于silverlight5,但它清楚地概述了使用Rx解决问题的方法/组合步骤。


1
Rx 绝对支持此功能,请参见 http://rxwiki.wikidot.com/101samples#toc29 - 关于取消它 - 请查看来自 Tasks 的 CancellationToken。 - Martin Ernst
1个回答

6
这是您可以使用ReactiveUI实现的方法:

以下是具体步骤:

IObservable<TheData> FetchNewData() 
{
    // TODO: Implement me
}

this.WhenAny(x => x.Property1, x => x.Property2, x => x.Property3, (x,y,z) => Unit.Default)
    .Throttle(TimeSpan.FromMilliseconds(200), RxApp.DeferredScheduler)
    .Select(x => FetchNewData())
    .Switch()    // We only care about the req corresp. to latest values of Prop1-3
    .ToProperty(this, x => x.Data);

更新:以下是如何确保同时只有一个运行的方法,但有一个注意事项,可能会得到无序结果。

this.WhenAny(x => x.Property1, x => x.Property2, x => x.Property3, (x,y,z) => Unit.Default)
    .Throttle(TimeSpan.FromMilliseconds(200), RxApp.DeferredScheduler)
    .Select(_ => Observable.Defer(() => FetchNewData()))
    .Merge(1)
    .ToProperty(this, x => x.Data);

您描述的行为可能并不理想,因为如果属性不断变化,您最终会得到一个旧请求队列 - 如果您创建了类似于“BufferingSwitch()”操作符的东西,它不会返回结果,直到确定没有更改,那将是很酷的写法。故事的寓意是,异步编程很复杂 :)

谢谢,乍一看似乎是我需要的。这对Silverlight有效吗?我找不到WhenAny扩展方法来尝试一下。 - Shaun Rowan
@ShaunRowan:该扩展来自ReactiveUI(http://www.reactiveui.net)。以前的版本(v2)支持Silverlight。当前版本(v3)不支持。特别是如果你正在使用MVVM,这是一个非常棒的库。 - Anderson Imes
谢谢,我会去看看的!由于某种原因,我的大脑刚刚将“ReactiveUI”替换为“响应式扩展”。(我不知道这个库。) - Shaun Rowan
2
@ShaunRowan 目前的版本一旦我行动起来修复它,就会支持SL5。 - Ana Betts
1
@ShaunRowan 这个有点棘手 - 你可以限制FetchNewData只能进行一个并发请求,但是这样可能会导致请求顺序混乱。我已经展示了如何实现。 - Ana Betts
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接