Asynchronous MvvmCross异步命令锁

Asynchronous MvvmCross异步命令锁,asynchronous,xamarin,command,mvvmcross,Asynchronous,Xamarin,Command,Mvvmcross,我的应用程序中有很多按钮。它们挨着放。所有方法都是IMvxAsyncCommand类型。在用户测试之后,我发现了一些错误匹配。我发现了重复的操作——几乎同时调用了两个不同的按钮 我所做的是创建我自己的SafeAncyncCommand类,并从MvxAsyncCommand中继承。我的目标是在执行之间创建延迟-我希望在以下情况下,在给定的延迟中防止双击0.5s 这是我的工作: public static class SafeCommandSettings { public

我的应用程序中有很多按钮。它们挨着放。所有方法都是IMvxAsyncCommand类型。在用户测试之后,我发现了一些错误匹配。我发现了重复的操作——几乎同时调用了两个不同的按钮

我所做的是创建我自己的SafeAncyncCommand类,并从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);
        }
    }
公共静态类安全命令设置
{
公共静态布尔CanExecute{get;私有集;}
公共静态时间间隔延迟=>TimeSpan.From毫秒(500);
静态安全命令设置()
{
CanExecute=true;
}
公共静态异步void Pause()
{
如果(!CanExecute)返回;
CanExecute=false;
等待任务。延迟(延迟);
CanExecute=true;
}
}
公共类SafeAsyncCommand:MvxAsyncCommand
{
public SafeAsyncCommand(Func execute,Func canExecute=null,bool allowConcurrentExecutions=false)
:base(执行、canExecute、allowConcurrentExecutions)
{
}
public SafeAsyncCommand(Func execute,Func canExecute=null,bool allowConcurrentExecutions=false)
:base(执行、canExecute、allowConcurrentExecutions)
{
}
受保护的重写异步任务ExecuteAsyncImpl(对象参数)
{
如果(!SafeCommandSettings.CanExecute)返回;
SafeCommandSettings.Pause();
wait base.ExecuteAsyncImpl(参数);
}
}
公共类SafeAsyncCommand:MvxAsyncCommand
{
public SafeAsyncCommand(Func execute,Func canExecute=null,bool allowConcurrentExecutions=false)
:base(执行、canExecute、allowConcurrentExecutions)
{
}
public SafeAsyncCommand(Func execute,Func canExecute=null,bool allowConcurrentExecutions=false)
:base(执行、canExecute、allowConcurrentExecutions)
{
}
受保护的重写异步任务ExecuteAsyncImpl(对象参数)
{
如果(!SafeCommandSettings.CanExecute)返回;
SafeCommandSettings.Pause();
wait base.ExecuteAsyncImpl(参数);
}
}
我认为这是可行的,但我看到用户能够再次这样做。我是否错过了一些关于异步方法或静态线程安全的知识


提前感谢

< P>而不是延迟事情,考虑Stephen Cleary使用“<代码> AsyncLock <代码> >查找>代码>互锁。CoprExchange < /C> > < /P> 从这里我可以看出,在您的情况下不应该使用静态
CanExecute
,因为它会立即使用“safe”命令锁定所有命令。
还有竞争条件的可能性,因为您没有更改
CanExecute
locked的值。

为了做到这一点,您可以利用
任务
的包装器,它监视不同的任务状态,您可以运行您的命令并执行类似的操作(请注意,您不需要将命令设置为
MvxAsyncCommand
):

因此,一旦异步进程启动,就不能再次执行该命令,以防止重复操作,并且可以在该任务完成后再次启动该命令

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

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

注意:如果您使用的是Mvx<5.5,您将不会有
MvxNotifyTask
,但您可以使用Stephen Cleary的done,它几乎与
MvxNotifyTask
相同,并且它是基于
MvxNotifyTask


HIH

谢谢你的回答:)我对这个答案进行了编码,如果按得很快,按钮将继续接收用户输入。我在文档中读到MvvmCross不太支持CanExecute for命令。您对此有何想法?在
MyLogicAsync
上,放置一个
wait Task.Yield()作为第一行,并让我知道这是否有助于解决您的问题
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
}