You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:20:55