基于MvvmCross自定义SafeAsyncCommand实现0.5秒防重复执行
解决MvxAsyncCommand重复触发问题:实现SafeAsyncCommand
我太懂这种痛点了——相邻按钮绑定的IMvxAsyncCommand被用户快速点击或误触时,会触发重复操作,搞不好还会导致数据冲突或异常。你想通过继承MvxAsyncCommand做一个带冷却机制的SafeAsyncCommand,下面是我整理的完善实现方案:
完整的SafeAsyncCommand实现
using System; using System.Threading; using System.Threading.Tasks; using MvvmCross.Commands; public class SafeAsyncCommand : MvxAsyncCommand { // 用信号量控制并发,确保同一时间只有一个命令能执行 private readonly SemaphoreSlim _executionLock = new SemaphoreSlim(1, 1); // 冷却时间,默认0.5秒 private readonly TimeSpan _cooldownDuration; // 无参数命令构造函数 public SafeAsyncCommand(Func<Task> execute, Func<bool> canExecute = null, TimeSpan? cooldown = null) : base(execute, canExecute) { _cooldownDuration = cooldown ?? TimeSpan.FromSeconds(0.5); } // 带参数命令构造函数 public SafeAsyncCommand(Func<object, Task> execute, Func<object, bool> canExecute = null, TimeSpan? cooldown = null) : base(execute, canExecute) { _cooldownDuration = cooldown ?? TimeSpan.FromSeconds(0.5); } public override async Task ExecuteAsync(object parameter = null) { // 立即尝试获取锁,拿不到说明在冷却/执行中,直接返回 if (!_executionLock.Wait(0)) return; try { // 执行原命令逻辑 await base.ExecuteAsync(parameter); // 执行完成后等待冷却时间 await Task.Delay(_cooldownDuration); } finally { // 无论成功失败都释放锁,避免死锁 _executionLock.Release(); } } // 释放资源,避免内存泄漏 protected override void Dispose(bool disposing) { if (disposing) { _executionLock.Dispose(); } base.Dispose(disposing); } }
关键设计说明
- 并发控制:用
SemaphoreSlim(1,1)实现轻量级的互斥锁,Wait(0)确保不会阻塞UI线程,拿不到锁直接拒绝新的执行请求。 - 可配置冷却:构造函数支持传入自定义冷却时间,默认0.5秒,适配不同业务场景。
- 异常安全:
try-finally块保证锁一定会被释放,哪怕命令执行抛出异常,也不会导致后续命令无法触发。 - API一致性:覆盖了
MvxAsyncCommand的两种重载(带/不带参数),替换原有命令时几乎不需要改代码。
使用示例
// 无参数命令,用默认0.5秒冷却 public ICommand SubmitCommand => new SafeAsyncCommand(ExecuteSubmit); // 带参数命令,自定义1秒冷却 public ICommand DeleteItemCommand => new SafeAsyncCommand(ExecuteDeleteItem, null, TimeSpan.FromSeconds(1)); private async Task ExecuteSubmit() { // 你的提交业务逻辑 } private async Task ExecuteDeleteItem(object parameter) { var itemId = (int)parameter; // 你的删除业务逻辑 }
额外注意点
- 冷却时间是从命令执行完成后开始计算的,不是点击时刻——如果命令本身执行耗时较长,不会在执行过程中允许新的触发,更符合业务逻辑。
- 保留了原
MvxAsyncCommand的线程调度特性,UI线程相关的操作不需要额外修改。
内容的提问来源于stack exchange,提问作者miechooy
相关产品推荐
相关产品推荐

