繁体   English   中英

如何使用 RX 实现超时序列?

[英]How to achieve sequence of timeouts with RX?

场景如下:如果设备在短时间内回调到服务器,则认为设备已连接。 我想创建一个类来封装跟踪此状态的功能。 调用设备时,应重置超时。 回调时,连接被确认,状态应设置为true ,如果回调超时,则应设置为false 但是下次调用应该可以重新设置超时时间,不管当前状态如何。

我想通过 RX 使用swithtimeout来实现这一点。 但我不知道为什么它停止工作。

public class ConnectionStatus
{
private Subject<bool> pending = new Subject<bool>();
private Subject<bool> connected = new Subject<bool>();

public bool IsConnected { get; private set; }

public ConnectionStatus(CancellationToken token, short timeoutSeconds = 15)
{
    pending.Select(outer => connected.Timeout(TimeSpan.FromSeconds(timeoutSeconds))) 
        .Switch()
        .Subscribe(_ => IsConnected = true, e => IsConnected = false, token);
}

public void ConfirmConnected()
{
    connected.OnNext(true);
}

public void SetPending()
{
    pending.OnNext(true);
}
}

这是“测试用例”:

var c = new ConnectionStatus(default(CancellationToken));

c.SetPending();
await Task.Delay(TimeSpan.FromSeconds(5));
c.ConfirmConnected();   
c.IsConnected.Dump(); // TRUE, OK

c.SetPending();
await Task.Delay(TimeSpan.FromSeconds(5));
c.ConfirmConnected();
c.IsConnected.Dump(); // TRUE, OK

c.SetPending();
await Task.Delay(TimeSpan.FromSeconds(20));
c.IsConnected.Dump(); // FALSE, OK
c.ConfirmConnected(); 
c.IsConnected.Dump(); // FALSE, OK

c.SetPending();
await Task.Delay(TimeSpan.FromSeconds(10));
c.ConfirmConnected(); 
c.IsConnected.Dump(); // FALSE, NOT OK!

我假设内部 observable 的超时也会停止外部 observable。 由于不再调用outer => lambda。 什么是正确的方法?

谢谢

这是在不使用.TimeOut情况下生成IsConnected值流的另一种方法:

public class ConnectionStatus
{
    private Subject<Unit> pending = new Subject<Unit>();
    private Subject<Unit> connected = new Subject<Unit>();

    public bool IsConnected { get; private set; }

    public ConnectionStatus(CancellationToken token, short timeoutSeconds = 15)
    {
        pending
            .Select(outer =>
                Observable.Amb(
                    connected.Select(_ => true),
                    Observable.Timer(TimeSpan.FromSeconds(timeoutSeconds)).Select(_ => false)))
            .Switch()
            .Subscribe(isConnected => IsConnected = isConnected, token);
    }

    public void ConfirmConnected()
    {
        connected.OnNext(Unit.Default);
    }

    public void SetPending()
    {
        pending.OnNext(Unit.Default);
    }
}

Observable.Amb操作符只是简单地从任何一个首先产生一个值的 observable 中获取一个值——它比使用异常编码更可取。

问题是Timeout本质上会导致异常炸毁 Rx 订阅。 触发超时后(如您所编码),将不会发送其他通知。 Rx 语法是您可以拥有 * OnNext消息,后跟一个OnCompleted或一个OnError 发送TimeoutOnError后,您将看不到更多消息。

您需要通过OnNext消息而不是OnError消息传递超时消息。 在您的旧代码中,您将任何OnError转换为 false,将任何OnNext转换为 true。 相反,您需要将正确的新IsConnected值嵌入到OnNext消息中。 以下是如何做到这一点:

public ConnectionStatus(CancellationToken token, short timeoutSeconds = 15)
{
    pending.Select(_ => connected
            .Timeout(TimeSpan.FromSeconds(timeoutSeconds))
            .Materialize()
            .Select(n => n.Kind == NotificationKind.OnError && n.Exception.GetType() == typeof(TimeoutException) 
                ? Notification.CreateOnNext(false)
                : n)
            .Dematerialize()
            .Take(1)
        )
        .Switch()
        .Subscribe(b => IsConnected = b, token);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM