MvvmCross 异步命令锁
MvvmCross Async command lock
我的应用程序中有很多按钮。它们并排放置。所有方法都是 IMvxAsyncCommand 类型。在用户完成测试后,我发现了一些不匹配的问题。我发现了重复的操作——几乎同时调用了两个不同的按钮。
我所做的是创建我自己的 SafeAsyncCommand class 并继承自 MvxAsyncCommand。我的目标是在执行之间创建延迟 - 我想防止在低于 0.5 秒的给定延迟中双击。
这是我的作品:
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);
}
}
我认为这行得通,但我看到用户能够再次这样做。我是否错过了一些关于异步方法或静态线程安全的知识?
提前致谢
不要拖延事情,考虑使用 Stephen Cleary 的 AsyncLock
或查找 Interlocked.CompareExchange
。
据我所知,您不应该在您的情况下使用静态 CanExecute
,因为它会立即锁定使用您的 "safe" 命令的所有命令。
并且存在竞争条件的可能性,因为您没有更改 CanExecute
locked.
的值
为此,您可以利用 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
[=21= 的条件]
同时检查 MvxNotifyTask
是否会引发 property-changed 通知,您可以在执行操作时在显示 "loading" 或类似内容的视图中订阅或绑定这些通知。
注意:如果您使用的 Mvx < 5.5,您将不会有 MvxNotifyTask
,但您可以使用由 Stephen Cleary 完成的 NotifyTaskCompletion,它几乎与 MvxNotifyTask
它来自 MvxNotifyTask
的基础。
高
我的应用程序中有很多按钮。它们并排放置。所有方法都是 IMvxAsyncCommand 类型。在用户完成测试后,我发现了一些不匹配的问题。我发现了重复的操作——几乎同时调用了两个不同的按钮。
我所做的是创建我自己的 SafeAsyncCommand class 并继承自 MvxAsyncCommand。我的目标是在执行之间创建延迟 - 我想防止在低于 0.5 秒的给定延迟中双击。
这是我的作品:
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);
}
}
我认为这行得通,但我看到用户能够再次这样做。我是否错过了一些关于异步方法或静态线程安全的知识?
提前致谢
不要拖延事情,考虑使用 Stephen Cleary 的 AsyncLock
或查找 Interlocked.CompareExchange
。
据我所知,您不应该在您的情况下使用静态 CanExecute
,因为它会立即锁定使用您的 "safe" 命令的所有命令。
并且存在竞争条件的可能性,因为您没有更改 CanExecute
locked.
为此,您可以利用 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
[=21= 的条件]
同时检查 MvxNotifyTask
是否会引发 property-changed 通知,您可以在执行操作时在显示 "loading" 或类似内容的视图中订阅或绑定这些通知。
注意:如果您使用的 Mvx < 5.5,您将不会有 MvxNotifyTask
,但您可以使用由 Stephen Cleary 完成的 NotifyTaskCompletion,它几乎与 MvxNotifyTask
它来自 MvxNotifyTask
的基础。
高