Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 但如果结果来得太晚,则放弃_C#_.net_System.reactive_Reactiveui_Rx.net - Fatal编程技术网

C# 但如果结果来得太晚,则放弃

C# 但如果结果来得太晚,则放弃,c#,.net,system.reactive,reactiveui,rx.net,C#,.net,System.reactive,Reactiveui,Rx.net,我正在编写一个用户界面,用户可以在其中键入搜索词,并不断更新列表,提供建议 我的第一个想法是,Rx原始油门是一个完美的匹配,但它让我有一半 这些建议需要一段时间才能获取,因此我在非UI线程上异步获取它们 问题是,如果用户再次键入af油门时间跨度,我想放弃/跳过/扔掉一个结果 例如: 时间开始,用户按下一个键:0毫秒 油门设置为100毫秒 提取需要200毫秒 150毫秒时,用户按下另一个键 现在,使用Throttle,第一次获取仍将继续,并填充gui建议列表。 我想了解的是,如何取消第一次获取

我正在编写一个用户界面,用户可以在其中键入搜索词,并不断更新列表,提供建议

我的第一个想法是,Rx原始油门是一个完美的匹配,但它让我有一半

这些建议需要一段时间才能获取,因此我在非UI线程上异步获取它们

问题是,如果用户再次键入af油门时间跨度,我想放弃/跳过/扔掉一个结果

例如:

  • 时间开始,用户按下一个键:0毫秒
  • 油门设置为100毫秒
  • 提取需要200毫秒
  • 150毫秒时,用户按下另一个键
现在,使用Throttle,第一次获取仍将继续,并填充gui建议列表。 我想了解的是,如何取消第一次获取,因为它不再相关了? 只有第二次按键才会触发gui更新

这是我试过的

(我使用ReactiveUI,但Q是关于Rx的)

public IEnumerable Results{get;set;}/[Reactive]pu
公共搜索opdrachtgevervm()
{
此.whenyValue(x=>x.FirstName,
x=>x.LastName
)
.节气门(时间跨度从毫秒(200))
.Subscribe(异步vm=>Results=wait PopulateGrid());
}
私有异步任务PopulateGrid()
{
返回等待任务。运行(
()=>_opdrachtgeversCache
.其中(x=>
x、 vNaam.Contains(名字)
&&x.vLastName.Contains(LastName)
)
);
}

如果我正确理解了您的需求,那么这可以以非常直接的方式完成,如果您稍微重构一下代码,就可以做到干净

首先,将名字和姓氏触发器设置为可观察对象。在下面的代码中,我使用了主题,但是如果您能够使用静态的可观察方法将它们“转换”为可观察的,那就更好了;e、 g.
可观察。FromEvent

然后将代码转换为可观察的结果。在下面的代码中,我使用了
Observable.Create
来返回
IEnumerable

最后,您可以使用Switch操作符订阅每个新的GetResults调用,并取消以前对GetResults的调用

听起来很复杂,但代码非常简单:

private Subject<string> _firstName = new Subject<string>();
private Subject<string> _lastName = new Subject<string>();

private Task<IEnumerable<string>> FetchResults(string firstName, string lastName, CancellationToken cancellationToken)
{
    // Fetch the results, respecting the cancellation token at the earliest opportunity
    return Task.FromResult(Enumerable.Empty<string>());
}

private IObservable<IEnumerable<string>> GetResults(string firstName, string lastName)
{
    return Observable.Create<IEnumerable<string>>(
        async observer =>
        {
            // Use a cancellation disposable to provide a cancellation token the the asynchronous method
            // When the subscription to this observable is disposed, the cancellation token will be cancelled.
            CancellationDisposable disposable = new CancellationDisposable();

            IEnumerable<string> results = await FetchResults(firstName, lastName, disposable.Token);

            if (!disposable.IsDisposed)
            {
                observer.OnNext(results);
                observer.OnCompleted();
            }

            return disposable;
        }
    );
}

private void UpdateGrid(IEnumerable<string> results)
{
    // Do whatever
}

private IDisposable ShouldUpdateGridWhenFirstOrLastNameChanges()
{
    return Observable
        // Whenever the first or last name changes, create a tuple of the first and last name
        .CombineLatest(_firstName, _lastName, (firstName, lastName) => new { FirstName = firstName, LastName = lastName })
        // Throttle these tuples so we only get a value after it has settled for 100ms
        .Throttle(TimeSpan.FromMilliseconds(100))
        // Select the results as an observable
        .Select(tuple => GetResults(tuple.FirstName, tuple.LastName))
        // Subscribe to the new results and cancel any previous subscription
        .Switch()
        // Use the new results to update the grid
        .Subscribe(UpdateGrid);
}
private Subject_firstName=new Subject();
私人受试者_lastName=新受试者();
私有任务FetchResults(string firstName、string lastName、CancellationToken CancellationToken)
{
//尽早获取与取消令牌相关的结果
返回Task.FromResult(Enumerable.Empty());
}
私有IObservable GetResults(字符串名、字符串名)
{
返回可观察的。创建(
异步观察者=>
{
//使用取消令牌为异步方法提供取消令牌
//当对该可观察对象的订阅被释放时,取消令牌将被取消。
CancellationDispossible=新的CancellationDispossible();
IEnumerable results=等待获取结果(firstName、lastName、disposable.Token);
如果(!disposed.IsDisposed)
{
observer.OnNext(结果);
observer.OnCompleted();
}
一次性退货;
}
);
}
私有void UpdateGrid(IEnumerable结果)
{
//做任何事
}
私有IDisposable应该UpdateGridWhenFirstorLastNameChanges()
{
可观测回波
//每当名字或姓氏更改时,创建一个名和姓氏的元组
.CombineLatest(_firstName,_lastName,(firstName,lastName)=>new{firstName=firstName,lastName=lastName})
//对这些元组进行节流,这样我们只能在它稳定100毫秒后得到一个值
.节气门(时间跨度从毫秒(100))
//选择结果作为可观察结果
.Select(tuple=>GetResults(tuple.FirstName,tuple.LastName))
//订阅新结果并取消以前的任何订阅
.Switch()
//使用新结果更新网格
.订阅(UpdateGrid);
}
快速提示:您应该将一个显式的调度器传递到节流阀中,这样您就可以使用TestScheduler有效地对代码进行单元测试


希望有帮助。

如果您将异步任务转化为可观察任务,这看起来像是
开关的经典用法:

this.WhenAnyValue(x => x.FirstName,
                  x => x.LastName
    )
    .Throttle(TimeSpan.FromMilliseconds(100)) 
    .Select(l => PopulateGrid().ToObservable())
    .Switch()
    .Subscribe(vm => Results = vm);

Throttle
应用于在用户键入时抑制呼叫。因此,可以根据您的喜好调整时间跨度。

WhenAnyValue
方法是ReactiveUI,即您建议的所有主题代码。您好@ibebs。正如Shlomo所指出的,我使用ReactiveUI来消除一些管道。我真的很感激你的回答,把它写出来!关于调度程序的提示我也会关注。这似乎是一个来自这一微妙行为的教科书示例似乎很重要:“请注意,当源可观察对象发出新可观察对象时,它将取消订阅先前发出的可观察对象,而不是当新可观察对象发出项目时。”实现方式也可以是,一旦发出一个项目,就会发生取消订阅(qood non)。选择的行为就是这个场景中需要的。啊,开关是的。当我读到它的时候,这是一个很有用的原语,它可以适合。我明天就试一试。开关的定义:将可观察序列的可观察序列转换为仅从最近的可观察序列产生值的可观察序列。
this.WhenAnyValue(x => x.FirstName,
                  x => x.LastName
    )
    .Throttle(TimeSpan.FromMilliseconds(100)) 
    .Select(l => PopulateGrid().ToObservable())
    .Switch()
    .Subscribe(vm => Results = vm);