[英]How to achieve sequence of timeouts with RX?
场景如下:如果设备在短时间内回调到服务器,则认为设备已连接。 我想创建一个类来封装跟踪此状态的功能。 调用设备时,应重置超时。 回调时,连接被确认,状态应设置为true ,如果回调超时,则应设置为false 。 但是下次调用应该可以重新设置超时时间,不管当前状态如何。
我想通过 RX 使用swith
和timeout
来实现这一点。 但我不知道为什么它停止工作。
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
。 发送Timeout
的OnError
后,您将看不到更多消息。
您需要通过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.