如何使用 ReactiveUI 在 ViewModel 停用时正确取消任务?
How to properly cancel a Task on ViewModel-deactivation with ReactiveUI?
在我的 MVVM 应用程序中,当一个 ViewModel 被激活时,一个任务开始建立网络连接,可能需要一些时间才能完成。此任务可取消:
private async Task ConnectAsync(CancellationToken cancellationToken = default)
{
...
}
我正在使用 IActivatableViewModel
在 ViewModel 激活时启动它:
// Constructor:
public SomeViewModel(...)
{
this.WhenActivated(disposable => {
Observable.StartAsync(ConnectAsync);
});
}
现在,当 ViewModel 在任务完成之前被停用时,取消这个长 运行 任务的推荐方法是什么?
我想到了这个:
this.WhenActivated(disposable => {
Observable.StartAsync(ConnectAsync).Subscribe().DisposeWith(disposable);
});
这是正确的解决方案还是有更好的解决方案?
提前致谢!
是的,您在代码片段中显示的代码看起来不错。但是,可能值得将 ConnectAsync
方法调用移动到 ReactiveCommand<TInput, TOutput>
(docs). If you do this, you get such perks as the ability to subscribe to ThrownExceptions
and IsExecuting
observables, and then display some loading indicators or error messages to keep your users informed about what the app is doing. Also, following the pattern described here,您可以通过另一个命令或事件取消 ReactiveCommand<TInput, TOutput>
。通过事件取消看起来像这样:
// ViewModel.cs
Cancel = ReactiveCommand.Create(() => { });
Connect = ReactiveCommand
.CreateFromObservable(
() => Observable
.StartAsync(ConnectAsync)
.TakeUntil(Cancel));
// View.xaml.cs
this.WhenActivated(disposable => {
this.Events() // Launch the long-running operation
.Loaded
.Select(args => Unit.Default)
.InvokeCommand(ViewModel, x => x.Connect)
.DisposeWith(disposable);
this.Events() // Stop that long-running operation
.Unloaded
.Select(args => Unit.Default)
.InvokeCommand(ViewModel, x => x.Cancel)
.DisposeWith(disposable);
});
在这里,我假设 ConnectAsync
是一种接受取消令牌并返回 Task
的方法。为了启用 this.Events()
魔法,您需要使用 Pharmacist, or to install one of the ReactiveUI.Events 包。但无论如何,如果你想依赖 WhenActivated
,你的选项看起来也不错,不需要 ThrownExceptions
,IsExecuting
等。如果你想使用命令并依赖 WhenActivated
,然后修改View.xaml.cs
代码:
// View.xaml.cs
this.WhenActivated(disposable => {
Connect.Execute().Subscribe();
Disposable
.Create(() => Cancel.Execute().Subscribe())
.DisposeWith(disposable);
});
我们不会处理 Execute()
返回的订阅,因为它们会在命令完成执行时被处理掉。希望这可以帮助! ✨
在我的 MVVM 应用程序中,当一个 ViewModel 被激活时,一个任务开始建立网络连接,可能需要一些时间才能完成。此任务可取消:
private async Task ConnectAsync(CancellationToken cancellationToken = default)
{
...
}
我正在使用 IActivatableViewModel
在 ViewModel 激活时启动它:
// Constructor:
public SomeViewModel(...)
{
this.WhenActivated(disposable => {
Observable.StartAsync(ConnectAsync);
});
}
现在,当 ViewModel 在任务完成之前被停用时,取消这个长 运行 任务的推荐方法是什么?
我想到了这个:
this.WhenActivated(disposable => {
Observable.StartAsync(ConnectAsync).Subscribe().DisposeWith(disposable);
});
这是正确的解决方案还是有更好的解决方案?
提前致谢!
是的,您在代码片段中显示的代码看起来不错。但是,可能值得将 ConnectAsync
方法调用移动到 ReactiveCommand<TInput, TOutput>
(docs). If you do this, you get such perks as the ability to subscribe to ThrownExceptions
and IsExecuting
observables, and then display some loading indicators or error messages to keep your users informed about what the app is doing. Also, following the pattern described here,您可以通过另一个命令或事件取消 ReactiveCommand<TInput, TOutput>
。通过事件取消看起来像这样:
// ViewModel.cs
Cancel = ReactiveCommand.Create(() => { });
Connect = ReactiveCommand
.CreateFromObservable(
() => Observable
.StartAsync(ConnectAsync)
.TakeUntil(Cancel));
// View.xaml.cs
this.WhenActivated(disposable => {
this.Events() // Launch the long-running operation
.Loaded
.Select(args => Unit.Default)
.InvokeCommand(ViewModel, x => x.Connect)
.DisposeWith(disposable);
this.Events() // Stop that long-running operation
.Unloaded
.Select(args => Unit.Default)
.InvokeCommand(ViewModel, x => x.Cancel)
.DisposeWith(disposable);
});
在这里,我假设 ConnectAsync
是一种接受取消令牌并返回 Task
的方法。为了启用 this.Events()
魔法,您需要使用 Pharmacist, or to install one of the ReactiveUI.Events 包。但无论如何,如果你想依赖 WhenActivated
,你的选项看起来也不错,不需要 ThrownExceptions
,IsExecuting
等。如果你想使用命令并依赖 WhenActivated
,然后修改View.xaml.cs
代码:
// View.xaml.cs
this.WhenActivated(disposable => {
Connect.Execute().Subscribe();
Disposable
.Create(() => Cancel.Execute().Subscribe())
.DisposeWith(disposable);
});
我们不会处理 Execute()
返回的订阅,因为它们会在命令完成执行时被处理掉。希望这可以帮助! ✨