具有特定值的Timer可观察

时间:2014-09-09 11:21:56

标签: c# system.reactive

不确定我的问题的标题,但希望我可以解释我想要做的事情。

我希望有一个Timer将一个值注入一个序列,但是当观察到一个特定值时。 我希望在序列中输入任何其他值时取消Timer。

public enum State
{
    Connected,
    Disconnected,
    DisconnectedRetryTimeout
}

var stateSubject = new Subject<State>();

var connectionStream = stateSubject.AsObservable();

var disconnectTimer = 
    Observable.Return(State.DisconnectedRetryTimeout)
        .Delay(TimeSpan.FromSeconds(30))
        .Concat(Observable.Never<State>());

var disconnectSignal =
    disconnectedTimer
        .TakeUntil(connectionStream.Where(s => s == State.Connected))
        .Repeat();

var statusObservable = 
    Observable.Merge(connectionStream, disconnectSignal)
        .DistinctUntilChanged();

所以当流中没有任何东西(即新的)没有计时器时。 当Connected | DisconnectedRetryTimeout添加无计时器时。 当Disconnected为add时,我希望计时器启动 如果在定时器触发之前已在流上连接,我希望取消定时器 定时器只应触发一次,直到再次收到断开连接。

对RX来说很新,但对这个想法用完了。

任何帮助非常感谢。

1 个答案:

答案 0 :(得分:3)

如果我正确理解了问题:我们从一个会发出ConnectedDisconnected条消息的状态流开始。如果DisconnectedRetryTimeout消息在流上停留30秒而没有显示Disconnected消息,则我们希望使用Connected消息来丰富此消息。

实现此目的的一种方法有以下想法:

将Connected / Disconnected流投影到DisconnectedRetryTimeout流中,如下所示:

  • 首先使用DistinctUntilChanged剥离重复项,因为我们只希望批处理的第一条Disconnected消息启动计时器。
  • 如果收到Disconnected,则将此事件投影为30秒后发出DisconnectedRetryTimeout的信息流
  • 如果收到已连接,只需投影无限且空的流(Observable.Never
  • 有了上述内容,我们最终得到了一个流媒体流,所以现在我们使用Switch通过始终获取最新的流来平展它。因此,如果在计时器处于运行状态时出现Connect,则Never流将替换计时器流。

现在我们可以将其与原始的去除流合并。请注意,我们发布了已删除的流,因为我们将订阅它两次 - 如果我们不这样做,并且源是冷的,那么我们可能会遇到问题。详细了解here

var stateSubject = new Subject<State>();

var state = stateSubject.DistinctUntilChanged().Publish().RefCount();

var disconnectTimer = state        
    .Select(x => x == State.Disconnected
        ? Observable.Timer(TimeSpan.FromSeconds(30))
            .Select(_ => State.DisconnectedRetryTimeout)
        : Observable.Never<State>())
    .Switch();

var statusObservable =
    state.Merge(disconnectTimer);

编辑:更简单的版本

您可以一次性执行此操作,然后删除发布步骤并合并 - 使用StartWith我们可以通过计时器流推送Disconnected事件,并使用Observable.Return我们可以通过替换空的Connected流来推送Never

var statusObservable = stateSubject
    .DistinctUntilChanged()
    .Select(x => x == State.Disconnected
        ? Observable.Timer(TimeSpan.FromSeconds(5))
                    .Select(_ => State.DisconnectedRetryTimeout)
                    .StartWith(State.Disconnected)
        : Observable.Return(State.Connected))
    .Switch();