简体   繁体   English

如何使用 ReactiveUI 正确取消 ViewModel 停用任务?

[英]How to properly cancel a Task on ViewModel-deactivation with ReactiveUI?

In my MVVM application, when a ViewModel gets activated, a Task gets started that establishes a network connection and could take some time to complete.在我的 MVVM 应用程序中,当 ViewModel 被激活时,将启动一个任务来建立网络连接,并且可能需要一些时间才能完成。 This Task is cancalable:此任务是可升级的:

private async Task ConnectAsync(CancellationToken cancellationToken = default)
{
    ...
}

I'm using IActivatableViewModel to start it on ViewModel-activation like that:我正在使用IActivatableViewModel在 ViewModel-activation 上启动它,如下所示:

// Constructor:
public SomeViewModel(...)
{
    this.WhenActivated(disposable => {
        Observable.StartAsync(ConnectAsync);
    });
}

Now what is the recommended method to cancel this long-running Task when the ViewModel gets deactivated before the Task completes?现在,当 ViewModel 在 Task 完成之前被停用时,取消这个长时间运行的 Task 的推荐方法是什么?

I came up with this:我想出了这个:

this.WhenActivated(disposable => {
    Observable.StartAsync(ConnectAsync).Subscribe().DisposeWith(disposable);
});

Is this the right solution or is there a better one?这是正确的解决方案还是有更好的解决方案?

Thank you in advance!先感谢您!

Yeah, the code like you show in your code snippet looks good.是的,您在代码片段中显示的代码看起来不错。 However, probably worth moving the ConnectAsync method call to a ReactiveCommand<TInput, TOutput> ( docs ).但是,可能值得将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.如果您这样做,您将获得诸如订阅ThrownExceptionsIsExecuting observables 的能力,然后显示一些加载指示器或错误消息,让您的用户了解应用程序正在做什么。 Also, following the pattern described here , you can cancel that ReactiveCommand<TInput, TOutput> via another command or event.此外,按照此处描述的模式,您可以通过另一个命令或事件取消该ReactiveCommand<TInput, TOutput> Cancelation via an event would look like this:通过事件取消看起来像这样:

// 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);
});

Here, I assume ConnectAsync is a method accepting a cancelation token and returning a Task .在这里,我假设ConnectAsync是一种接受取消令牌并返回Task In order to enable the this.Events() magic, you need to either use Pharmacist , or to install one of the ReactiveUI.Events packages.为了启用this.Events()魔法,您需要使用Pharmacist ,或者安装ReactiveUI.Events包之一。 But anyway, your option looks good as well if you want to rely on WhenActivated , don't need ThrownExceptions , IsExecuting etc. If you'd like to use commands and rely on WhenActivated , then modify the View.xaml.cs code:但无论如何,如果您想依赖WhenActivated ,不需要ThrownExceptionsIsExecuting等,您的选项看起来也不错。如果您想使用命令并依赖WhenActivated ,请修改View.xaml.cs代码:

// View.xaml.cs
this.WhenActivated(disposable => {
    Connect.Execute().Subscribe();
    Disposable
        .Create(() => Cancel.Execute().Subscribe())
        .DisposeWith(disposable);
});

We aren't disposing of the subscriptions returned by Execute() because they'll get disposed anyway when the commands complete their execution.我们不会处理Execute()返回的订阅,因为当命令完成它们的执行时,它们无论如何都会被处理。 Hope this helps!希望这可以帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM