MvvmCross异步命令锁定

时间:2018-03-19 17:17:15

标签: asynchronous xamarin command mvvmcross

我的应用程序中有很多按钮。它们彼此相邻。所有方法都是IMvxAsyncCommand类型。在用户完成测试后,我发现了一些不匹配的问题。我发现了重复的操作 - 几乎同时调用了两个不同的按钮。

我做了什么创建了我自己的SafeAsyncCommand类,并且来自MvxAsyncCommand。我的目标是在执行之间创建延迟 - 我希望防止在下面的情况下在给定的延迟中双击0.5s。

我的工作是:

public static class SafeCommandSettings
    {
        public static bool CanExecute { get; private set; }
        public static TimeSpan Delay => TimeSpan.FromMilliseconds(500);

        static SafeCommandSettings()
        {
            CanExecute = true;
        }

        public static async void Pause()
        {
            if (!CanExecute) return;

            CanExecute = false;
            await Task.Delay(Delay);
            CanExecute = true;
        }
    }

    public class SafeAsyncCommand : MvxAsyncCommand
    {
        public SafeAsyncCommand(Func<Task> execute, Func<bool> canExecute = null, bool allowConcurrentExecutions = false)
            : base(execute, canExecute, allowConcurrentExecutions)
        {
        }

        public SafeAsyncCommand(Func<CancellationToken, Task> execute, Func<bool> canExecute = null, bool allowConcurrentExecutions = false)
            : base(execute, canExecute, allowConcurrentExecutions)
        {
        }

        protected override async Task ExecuteAsyncImpl(object parameter)
        {
            if (!SafeCommandSettings.CanExecute) return;

            SafeCommandSettings.Pause();
            await base.ExecuteAsyncImpl(parameter);
        }
    }

    public class SafeAsyncCommand<T> : MvxAsyncCommand<T>
    {
        public SafeAsyncCommand(Func<T, Task> execute, Func<T, bool> canExecute = null, bool allowConcurrentExecutions = false)
            : base(execute, canExecute, allowConcurrentExecutions)
        {
        }

        public SafeAsyncCommand(Func<T, CancellationToken, Task> execute, Func<T, bool> canExecute = null, bool allowConcurrentExecutions = false)
            : base(execute, canExecute, allowConcurrentExecutions)
        {
        }

        protected override async Task ExecuteAsyncImpl(object parameter)
        {
            if (!SafeCommandSettings.CanExecute) return;

            SafeCommandSettings.Pause();
            await base.ExecuteAsyncImpl(parameter);
        }
    }

我认为这是有效的,但我看到用户能够再次这样做。我是否会错过有关异步方法或静态线程安全的一些知识?

提前致谢

2 个答案:

答案 0 :(得分:2)

考虑使用Stephen Cleary的AsyncLock或查找Interlocked.CompareExchange,而不是推迟事情。

据我所知,你不应该在你的情况下使用静态CanExecute,因为它会立即使用你的“安全”命令锁定所有命令。 并且存在竞争条件的可能性,因为您没有更改CanExecute已锁定的值。

答案 1 :(得分:1)

为了做到这一点,你可以利用MvxNotifyTask,它是监视不同任务状态的Task的包装,你将按照你的命令运行并做这样的事情(通知您不需要命令为MvxAsyncCommand):

public MvxNotifyTask MyNotifyTaskWrapper { get; private set; }

public MvxCommand MyCommand { get; private set; }

private void InitializeCommands()
{
    // this command is executed only if the task has not started (its wrapper is null) or the task is not in progress (its wrapper is not IsNotCompleted)
    this.MyCommand = new MvxCommand(() => this.MyNotifyTaskWrapper = MvxNotifyTask.Create(() => this.MyLogicAsync()),
                                    () => this.MyNotifyTaskWrapper == null || !this.MyNotifyTaskWrapper.IsNotCompleted);
}

private async Task MyLogicAsync()
{
    // my async logic
}

因此,只要异步进程启动,就无法再次执行该命令以防止重复操作,并且可以在该任务完成时再次启动它。

如果在运行某些任务时必须禁用多个命令,只需在不同命令上添加相同的CanExecute条件或混合不同MvxNotifyTask的条件

同时检查MvxNotifyTask引发属性更改的通知,您可以在视图中订阅或绑定这些通知,在执行操作时显示“加载”或类似内容。

注意:如果您使用的是Mvx&lt; 5.5您将不会MvxNotifyTask,但您可以使用Stephen Cleary完成的NotifyTaskCompletionMvxNotifyTask几乎相同,并且它来自MvxNotifyTask所基于的位置。< / em>的

HIH