C# 如果在下一个事件到达之前未完成选择,如何取消接收中的选择
我有以下设置C# 如果在下一个事件到达之前未完成选择,如何取消接收中的选择,c#,system.reactive,C#,System.reactive,我有以下设置 IObservable<Data> source = ...; source .Select(data=>VeryExpensiveOperation(data)) .Subscribe(data=>Console.WriteLine(data)); 因此,每次调用lambda时,都会使用cancelToken进行调用,cancelToken可以 用于管理取消任务。然而,现在我们正在混合任务、取消令牌和接收。 不太确定如何将其全部装配在一
IObservable<Data> source = ...;
source
.Select(data=>VeryExpensiveOperation(data))
.Subscribe(data=>Console.WriteLine(data));
因此,每次调用lambda时,都会使用cancelToken进行调用,cancelToken可以
用于管理取消任务
。然而,现在我们正在混合任务、取消令牌和接收。
不太确定如何将其全部装配在一起。任何建议
计算出如何使用XUnit测试操作员的奖励积分:)
第一次尝试
public static IObservable<U> SelectWithCancelation<T, U>( this IObservable<T> This, Func<CancellationToken, T, Task<U>> fn )
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
return This
.ObserveOn(Scheduler.Default)
.Select(v=>{
tokenSource.Cancel();
tokenSource=new CancellationTokenSource();
return new {tokenSource.Token, v};
})
.SelectMany(o=>Observable.FromAsync(()=>fn(o.Token, o.v)));
}
public static IObservable selectwithcancellation(this IObservable this,Func fn)
{
CancellationTokenSource tokenSource=新的CancellationTokenSource();
还这个
.ObserveOn(Scheduler.Default)
.选择(v=>{
tokenSource.Cancel();
tokenSource=新的CancellationTokenSource();
返回新的{tokenSource.Token,v};
})
.SelectMany(o=>Observable.fromsync(()=>fn(o.Token,o.v));
}
还没有测试。我希望未完成的任务会生成一个IObservable,该IObservable在完成时不会触发任何
OnNext
事件。您必须将非常昂贵的操作
建模为可取消的异步操作。无论是任务
还是IObservable
。我假设这是一个带有取消令牌的任务:
Task<TResult> VeryExpensiveOperationAsync<TSource, TResult>(TSource item, CancellationToken token);
Select
只创建了一个延迟的可观察对象,当订阅时,它将创建一个令牌并启动操作。如果在操作完成之前取消订阅了observable,则令牌将被取消
开关
订阅来自选择
的每个新观察对象,取消订阅之前订阅的观察对象
这有你想要的效果
另外,这很容易测试。只需提供一个模拟源和一个使用单元测试提供的taskcompletionsource
的模拟VeryExpensiveOperation
,这样单元测试就可以精确控制何时生成新的源项以及何时完成任务。大概是这样的:
void SomeTest()
{
// create a test source where the values are how long
// the mock operation should wait to do its work.
var source = _testScheduler.CreateColdObservable<int>(...);
// records the actions (whether they completed or canceled)
List<bool> mockActionsCompleted = new List<bool>();
var resultStream = source.SelectWithCancellation((token, delay) =>
{
var tcs = new TaskCompletionSource<string>();
var tokenRegistration = new SingleAssignmentDisposable();
// schedule an action to complete the task
var d = _testScheduler.ScheduleRelative(delay, () =>
{
mockActionsCompleted.Add(true);
tcs.SetResult("done " + delay);
// stop listening to the token
tokenRegistration.Dispose();
});
// listen to the token and cancel the task if the token signals
tokenRegistration.Disposable = token.Register(() =>
{
mockActionsCompleted.Add(false);
tcs.TrySetCancelled();
// cancel the scheduled task
d.Dispose();
});
return tcs.Task;
});
// subscribe to resultStream
// start the scheduler
// assert the mockActionsCompleted has the correct sequence
// assert the results observed were what you expected.
}
void SomeTest()
{
//创建一个测试源,其中的值是多长
//模拟操作应等待完成其工作。
var source=_testScheduler.CreateColdObservable(…);
//记录操作(无论是已完成还是已取消)
List mockActionsCompleted=新建列表();
var resultStream=source.SelectWithCancellation((令牌,延迟)=>
{
var tcs=new TaskCompletionSource();
var tokenRegistration=new SingleAssignmentDisposable();
//安排一项操作以完成任务
var d=\u testScheduler.ScheduleRelative(延迟,()=>
{
mockActionsCompleted.Add(true);
tcs.设置结果(“完成”+延迟);
//停止收听令牌
tokenRegistration.Dispose();
});
//侦听令牌并在令牌发出信号时取消任务
tokenRegistration.Dispossible=token.Register(()=>
{
mockActionsCompleted.Add(false);
tcs.TrySetCancelled();
//取消计划的任务
d、 处置();
});
返回tcs.Task;
});
//订阅resultStream
//启动调度程序
//断言mockActionsCompleted具有正确的序列
//断言观察到的结果是您所期望的。
}
由于动态调度的新操作,使用testScheduler.Start()
可能会遇到问题。使用testScheduler.AdvanceBy(1)
进行while循环可能会更好 为什么不使用油门呢
油门停止事件流,直到在指定的时间段内不再产生事件。例如,如果将文本框的TextChanged事件限制为.5秒,则在用户停止键入0.5秒之前,不会传递任何事件。这在搜索框中很有用,在搜索框中,您不希望在每次击键后开始新的搜索,而是希望等待用户暂停
SearchTextChangedObservable=Observable.FromEventPattern(this.textBox,“TextChanged”);
_currentSubscription=SearchTextChangedObservable.Throttle(TimeSpan.FromSeconds(.5)).ObserveOnDispatcher
我不太明白令牌在哪里被取消。当Switch取消订阅上一个内部可观察对象时,是否自动完成此操作?DeferAsync
在可观察对象被订阅时创建令牌。然后,如果在任务生成真正的可观察对象之前未订阅该可观察对象,它将取消该标记(让您知道您不需要继续工作来生成真正的可观察对象)Switch
不断订阅到达的新observable,并取消订阅上一个observable,这会触发上一个observable的取消。我无法编写一个非常昂贵的模拟操作。问题是我必须使用Task.Run来创建它,这会将它放在TestScheduler上下文之外的另一个线程上。然后我没有得到正确的结果。我添加了一个我所想的例子。你用了错误的方法。事件不会来得太快。不需要节流。处理过程非常昂贵,如果有新的数据可用,应该取消。实际上,我的意思是在开始昂贵的处理之前进行节流,这样我们只有在知道它有时间完成时才能运行它。然而,我现在可以看到,与Brandon的实现相比,这只是对它需要完成多长时间的一个初步估计。
source
.Select(item => Observable.DeferAsync(async token =>
{
// do not yield the observable until after the operation is completed
// (ie do not just do VeryExpensiveOperation(...).ToObservable())
// because DeferAsync() will dispose of the token source as soon
// as you provide the observable (instead of when the observable completes)
var result = await VeryExpensiveOperationAsync(item, token);
return Observable.Return(result);
})
.Switch();
void SomeTest()
{
// create a test source where the values are how long
// the mock operation should wait to do its work.
var source = _testScheduler.CreateColdObservable<int>(...);
// records the actions (whether they completed or canceled)
List<bool> mockActionsCompleted = new List<bool>();
var resultStream = source.SelectWithCancellation((token, delay) =>
{
var tcs = new TaskCompletionSource<string>();
var tokenRegistration = new SingleAssignmentDisposable();
// schedule an action to complete the task
var d = _testScheduler.ScheduleRelative(delay, () =>
{
mockActionsCompleted.Add(true);
tcs.SetResult("done " + delay);
// stop listening to the token
tokenRegistration.Dispose();
});
// listen to the token and cancel the task if the token signals
tokenRegistration.Disposable = token.Register(() =>
{
mockActionsCompleted.Add(false);
tcs.TrySetCancelled();
// cancel the scheduled task
d.Dispose();
});
return tcs.Task;
});
// subscribe to resultStream
// start the scheduler
// assert the mockActionsCompleted has the correct sequence
// assert the results observed were what you expected.
}
SearchTextChangedObservable = Observable.FromEventPattern<TextChangedEventArgs>(this.textBox, "TextChanged");
_currentSubscription = SearchTextChangedObservable.Throttle(TimeSpan.FromSeconds(.5)).ObserveOnDispatcher