取消并重新执行 ReactiveCommand
Cancelling and re-executing ReactiveCommand
我正在努力处理 ReactiveUI 用例,我觉得它非常简单,必须 "out-of-the-box" 支持它。但是我找不到。
该方案是具有以下功能的基本搜索界面:
- 用户在其中输入搜索文本的搜索字符串文本框
- 显示结果的结果文本框
- 显示搜索正在进行的指示器
搜索应该是这样的:
- 搜索字符串 TextBox 被限制,因此在 500 毫秒后
不活动,启动搜索操作。
- 每次启动新搜索时,都应取消任何正在进行的搜索操作。
基本上我正在尝试扩展 "Compelling example" 以在开始新命令之前取消当前正在执行的命令。
看起来很简单?是的,但我无法使用 ReactiveCommand 正确处理。这是我的:
var searchTrigger = this.WhenAnyValue(vm => vm.SearchString)
.Throttle(TimeSpan.FromMilliseconds(500))
.Publish().RefCount();
var searchCmd = ReactiveCommand.CreateFromObservable(
() => Observable
.StartAsync(ct => CancellableSearch(SearchString, ct))
.TakeUntil(searchTrigger));
searchCmd.ToPropertyEx(this, vm => vm.Result);
searchCmd.IsExecuting.ToPropertyEx(this, vm => vm.IsSearching);
searchTrigger.Subscribe(_ => searchCmd.Execute(Unit.Default).Subscribe());
以上代码在除searchCmd.IsExecuting
以外的所有方面都有效。无论 searchCmd.CanExecute
的状态如何,我都会开始新的搜索。这使得 IsExecuting
不可靠,因为它假定命令的串行操作。而且我不能使用 InvokeCommand
而不是 Execute
,因为这样在搜索过程中就不会开始新的搜索。
我目前有一个没有 ReactiveCommand
的工作解决方案。但我有一种强烈的感觉,应该使用 ReactiveCommand
以直接的方式支持这个简单的用例。我错过了什么?
AFAICT Rx7 并没有真正处理这种重叠执行。所有消息最终都会通过,但不会使您的 IsExecuting 始终如一。 Rx6 使用了一个 In-flight 计数器,因此处理了重叠的执行,但 Rx7 将它完全简化了。最有可能是为了性能和可靠性(但我只是猜测)。因为任务不会立即取消,所以第一个命令将在第二个命令启动后完成,这导致 IsExecuting 从 true 切换到 false 到 true 到 false。但是当消息赶上时,从假到真再到假的中间转换会立即发生。我知道你说过你有一个非反应性命令在工作,但我认为这是一个通过等待第一个命令完成或完成取消来与反应性命令一起工作的版本。等到 Task 实际取消的一个好处是,您可以确信您没有两只手放在 cookie 罐中 :-) 这对您的情况可能无关紧要,但在某些情况下可能会很好。
//Fires an event right away so search is cancelled faster
var searchEntered = this.WhenAnyValue(vm => vm.SearchString)
.Where(x => !String.IsNullOrWhiteSpace(x))
.Publish()
.RefCount();
ReactiveCommand<string, string> searchCmd = ReactiveCommand.CreateFromObservable<string, string>(
(searchString) => Observable.StartAsync(ct => CancellableSearch(SearchString, ct))
.TakeUntil(searchEntered));
//if triggered wait for IsExecuting to transition back to false before firing command again
var searchTrigger =
searchEntered
.Throttle(TimeSpan.FromMilliseconds(500))
.Select(searchString => searchCmd.IsExecuting.Where(e => !e).Take(1).Select(_ => searchString))
.Publish()
.RefCount();
_IsSearching =
searchCmd.IsExecuting
.ToProperty(this, vm => vm.IsSearching);
searchTrigger
.Switch()
.InvokeCommand(searchCmd);
我正在努力处理 ReactiveUI 用例,我觉得它非常简单,必须 "out-of-the-box" 支持它。但是我找不到。
该方案是具有以下功能的基本搜索界面:
- 用户在其中输入搜索文本的搜索字符串文本框
- 显示结果的结果文本框
- 显示搜索正在进行的指示器
搜索应该是这样的:
- 搜索字符串 TextBox 被限制,因此在 500 毫秒后 不活动,启动搜索操作。
- 每次启动新搜索时,都应取消任何正在进行的搜索操作。
基本上我正在尝试扩展 "Compelling example" 以在开始新命令之前取消当前正在执行的命令。
看起来很简单?是的,但我无法使用 ReactiveCommand 正确处理。这是我的:
var searchTrigger = this.WhenAnyValue(vm => vm.SearchString)
.Throttle(TimeSpan.FromMilliseconds(500))
.Publish().RefCount();
var searchCmd = ReactiveCommand.CreateFromObservable(
() => Observable
.StartAsync(ct => CancellableSearch(SearchString, ct))
.TakeUntil(searchTrigger));
searchCmd.ToPropertyEx(this, vm => vm.Result);
searchCmd.IsExecuting.ToPropertyEx(this, vm => vm.IsSearching);
searchTrigger.Subscribe(_ => searchCmd.Execute(Unit.Default).Subscribe());
以上代码在除searchCmd.IsExecuting
以外的所有方面都有效。无论 searchCmd.CanExecute
的状态如何,我都会开始新的搜索。这使得 IsExecuting
不可靠,因为它假定命令的串行操作。而且我不能使用 InvokeCommand
而不是 Execute
,因为这样在搜索过程中就不会开始新的搜索。
我目前有一个没有 ReactiveCommand
的工作解决方案。但我有一种强烈的感觉,应该使用 ReactiveCommand
以直接的方式支持这个简单的用例。我错过了什么?
AFAICT Rx7 并没有真正处理这种重叠执行。所有消息最终都会通过,但不会使您的 IsExecuting 始终如一。 Rx6 使用了一个 In-flight 计数器,因此处理了重叠的执行,但 Rx7 将它完全简化了。最有可能是为了性能和可靠性(但我只是猜测)。因为任务不会立即取消,所以第一个命令将在第二个命令启动后完成,这导致 IsExecuting 从 true 切换到 false 到 true 到 false。但是当消息赶上时,从假到真再到假的中间转换会立即发生。我知道你说过你有一个非反应性命令在工作,但我认为这是一个通过等待第一个命令完成或完成取消来与反应性命令一起工作的版本。等到 Task 实际取消的一个好处是,您可以确信您没有两只手放在 cookie 罐中 :-) 这对您的情况可能无关紧要,但在某些情况下可能会很好。
//Fires an event right away so search is cancelled faster
var searchEntered = this.WhenAnyValue(vm => vm.SearchString)
.Where(x => !String.IsNullOrWhiteSpace(x))
.Publish()
.RefCount();
ReactiveCommand<string, string> searchCmd = ReactiveCommand.CreateFromObservable<string, string>(
(searchString) => Observable.StartAsync(ct => CancellableSearch(SearchString, ct))
.TakeUntil(searchEntered));
//if triggered wait for IsExecuting to transition back to false before firing command again
var searchTrigger =
searchEntered
.Throttle(TimeSpan.FromMilliseconds(500))
.Select(searchString => searchCmd.IsExecuting.Where(e => !e).Take(1).Select(_ => searchString))
.Publish()
.RefCount();
_IsSearching =
searchCmd.IsExecuting
.ToProperty(this, vm => vm.IsSearching);
searchTrigger
.Switch()
.InvokeCommand(searchCmd);