我想做的宣言如下:
// Checks input source for timeouts, based on the number of elements received
// from clock since the last one received from source.
// The two selectors are used to generate output elements.
public static IObservable<R> TimeoutDetector<T1,T2,R>(
this IObservable<T1> source,
IObservable<T2> clock,
int countForTimeout,
Func<R> timedOutSelector,
Func<T1, R> okSelector)
ascii中的大理石图很难,但这里有:
source --o---o--o-o----o-------------------o---
clock ----x---x---x---x---x---x---x---x---x---
output --^---^--^-^----^-----------!-------^---
我尝试过寻找可以使用Observable
和source
的现有clock
函数,但大多数组合函数都依赖于每个'(And
,Zip
),或者他们从'缺失的'(CombineLatest
)重新返回'之前'的值,或者它们离我的距离太远需要(Amb
,GroupJoin
,Join
,Merge
,SelectMany
,Timeout
)。 Sample
看起来很接近,但我不想将源吞吐量限制为时钟速率。
所以现在我不得不试图填补这里的巨大差距:
return new AnonymousObservable<R>(observer =>
{
//One observer, two observables??
});
对不起,'你试过的'部分在这里有点弱:让我说我已经尝试过了!我不是要求全面实施,只是:
答案 0 :(得分:6)
我知道你并没有要求全面实施,但我认为这是一个解决方案:
public static IObservable<TR> TimeoutDetector<T1, T2, TR>(
this IObservable<T1> source,
IObservable<T2> clock,
int countForTimeout,
Func<TR> timedOutSelector,
Func<T1, TR> okSelector)
{
return source
.Select(i => clock.Take(countForTimeout).LastAsync())
.Switch().Select(_ => timedOutSelector())
.Merge(source.Select(okSelector));
}
它的工作原理如下 - 我注意到你的输出是okSelector投射的源,与超时事件合并。所以我专注于产生超时事件,因为其余的很容易。
这个想法是每次发出源时创建倒计时,并在每个时钟脉冲上递减倒计时。如果源发出,我们中止倒计时,否则当倒计时达到0时,我们会产生timedOut事件。
打破它:
countForTimeout
元素的流中 - 请注意,时钟流必须是“热”可观察对象,因为我们在每个countDown事件上订阅它。时钟流很热是很正常的。如果这件事发生了,我们就会超时。Switch
将丢弃除最新的倒计时流以外的所有内容。Select
投放到timedOut事件。这是我使用的单元测试,旨在与您的大理石图非常相似(nuget rx-testing&amp; nunit用于编译必要的库):
[Test]
public void AKindOfTimeoutTest()
{
var scheduler = new TestScheduler();
var clockStream = scheduler.CreateHotObservable(
OnNext(100, Unit.Default),
OnNext(200, Unit.Default),
OnNext(300, Unit.Default),
OnNext(400, Unit.Default),
OnNext(500, Unit.Default),
OnNext(600, Unit.Default),
OnNext(750, Unit.Default), /* make clock funky! */
OnNext(800, Unit.Default),
OnNext(900, Unit.Default));
var sourceStream = scheduler.CreateColdObservable(
OnNext(50, 1),
OnNext(150, 2),
OnNext(250, 3),
OnNext(275, 4),
OnNext(400, 5),
OnNext(900, 6));
Func<int> timedOutSelector = () => 0;
Func<int, int> okSelector = i => i;
var results = scheduler.CreateObserver<int>();
sourceStream.TimeoutDetector(clockStream, 3, timedOutSelector, okSelector)
.Subscribe(results);
scheduler.Start();
results.Messages.AssertEqual(
OnNext(50, 1),
OnNext(150, 2),
OnNext(250, 3),
OnNext(275, 4),
OnNext(400, 5),
OnNext(750, 0),
OnNext(900, 6));
}
}
尝试回答您的具体问题:
答案 1 :(得分:2)
这是我提到的Observable.Create方法(相同的测试工作):
public static IObservable<TR> TimeoutDetector<T1, T2, TR>(
this IObservable<T1> source,
IObservable<T2> clock,
int countForTimeout,
Func<TR> timedOutSelector,
Func<T1, TR> okSelector)
{
return Observable.Create<TR>(observer =>
{
var counter = countForTimeout;
var timeoutSub = clock.Subscribe(_ =>
{
var count = Interlocked.Decrement(ref counter);
if (count == 0)
{
observer.OnNext(timedOutSelector());
}
},
observer.OnError,
observer.OnCompleted);
var sourceSub = source.Subscribe(
i =>
{
Interlocked.Exchange(ref counter, countForTimeout);
observer.OnNext(okSelector(i));
},
observer.OnError,
observer.OnCompleted);
return new CompositeDisposable(sourceSub, timeoutSub);
});
}
请注意,Observable.Create对于确保使用正确的Rx语法非常有帮助(即流发出OnNext *(OnError | OnCompleted)? - 这意味着我可以稍微放松一下发送OnError或OnCompleted一次。
答案 2 :(得分:0)
我想出了这个,这不如詹姆斯的回答那么漂亮。
public static IObservable<R> TimeoutDetector2<T1, T2, R>(
this IObservable<T1> source,
IObservable<T2> clock, int maxDiff,
Func<R> timedOutSelector, Func<T1, R> okSelector)
{
return new AnonymousObservable<R>(observer =>
{
int counter = 0;
object gate = new object();
bool error = false;
bool completed = false;
bool timedOut = false;
var sourceSubscription = source.Subscribe(
x =>
{
lock(gate)
{
if(!error && !completed) observer.OnNext(okSelector(x));
counter = 0;
timedOut = false;
}
},
ex =>
{
lock(gate)
{
error = true;
if(!completed) observer.OnError(ex);
}
},
() =>
{
lock(gate)
{
completed = true;
if(!error) observer.OnCompleted();
}
});
var clockSubscription = clock.Subscribe(
x =>
{
lock(gate)
{
counter = counter + 1;
if(!error && !completed && counter > maxDiff && !timedOut)
{
timedOut = true;
observer.OnNext(timedOutSelector());
}
}
},
ex =>
{
lock(gate)
{
error = true;
if(!completed) observer.OnError(ex);
}
},
() =>
{
lock(gate)
{
completed = true;
if(!error) observer.OnCompleted();
}
});
//need to return a subscription
return new CompositeDisposable(sourceSubscription, clockSubscription);
}).Publish().RefCount(); // prevent subscribers provoking more than one subscription to source and clock
}
答案 3 :(得分:0)
这当然是一个老问题。我一直在寻找比race(throwError('timedout').pipe(delay(10000)), yourObs$)
更高级的功能,以便可以取消超时逻辑。
我想知道超时是否等同于:
react-native start
当然,这里显示的'throwError'可以取消。
如果您想知道为什么-我有一些“步骤”受可观察的链条控制,并且我有超时。但是,如果其中一个步骤包括打开对话框,那么我想取消超时!