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

Blazor中两个独立任务同步:确保一个完成后再执行另一个

Blazor跨组件任务同步:解决标签切换与表单关闭时的RichEdit内容丢失问题

问题描述

在Blazor项目中遇到跨组件任务同步的棘手问题:两个由不同组件触发的任务调用顺序不可控,可能出现「需要等待的任务还没执行,必须完成的任务已经结束」的情况,导致功能异常。

核心场景:标签切换时的内容丢失

用DxFormLayoutTabPages实现标签页,它的ActiveTabIndexChanged事件和标签页内DxRichEdit的LostFocus事件存在冲突:

  • 用户从含RichEdit的标签切换到其他标签时,两个事件都会触发
  • 如果ActiveTabIndexChanged先执行完成,RichEdit会被直接释放,导致LostFocus里调用的await RichEditDesc.SaveDocumentAsync()无法触发DocumentContentChanged,最终丢失编辑的内容

其他同类问题

用户在RichEdit标签页点击保存时,AppointmentFormClosing事件和LostFocus事件也有同样的同步冲突,需要一套统一的方案解决这类问题。

当前临时方案的弊端

之前用Task.Delay(1000)做延迟,存在两个大问题:

  • 会导致UI卡顿,影响用户体验
  • 延迟时长完全不可靠,不同性能的环境下可能失效
  • 而且事件触发顺序可能是ActiveTabIndexChanged先,也可能是LostFocus先,加上用户可多次切换标签,初始化信号量的方式根本行不通

相关Razor代码

<DxScheduler AppointmentFormClosing="AppointmentFormClosing">
  <AppointmentFormLayout>
    <DxFormLayoutTabPages ActiveTabIndex="@ActiveIndex" ActiveTabIndexChanged="OnActiveTabIndexChanged">
      <DxFormLayoutTabPage Caption="Details">
        <DxRichEdit LostFocus="OnRichEditDescLostFocus" ModifiedChanged="OnDocumentDescModified" DocumentContentChanged="OnDocumentDescChanged">
      </DxFormLayoutTabPage>
    </DxFormLayoutTabPages>
  </AppointmentFormLayout>
</DxScheduler>

解决方案:用SemaphoreSlim实现可靠同步

针对这种场景,SemaphoreSlim是比Task.Delay靠谱得多的选择,它能确保关键任务执行完再跑后续操作,还支持多次触发的情况,具体实现步骤如下:

1. 定义同步锁

在组件的代码部分声明一个SemaphoreSlim对象,初始只允许一个任务执行,组件销毁时记得释放资源:

private readonly SemaphoreSlim _richEditSyncLock = new SemaphoreSlim(1, 1);

public override void Dispose()
{
    _richEditSyncLock.Dispose();
    base.Dispose();
}

2. 改造RichEdit失焦事件

在OnRichEditDescLostFocus里,先获取锁再执行保存操作,确保保存过程中不会有其他操作销毁RichEdit:

private async Task OnRichEditDescLostFocus()
{
    await _richEditSyncLock.WaitAsync();
    try
    {
        if (RichEditDesc.IsModified)
        {
            await RichEditDesc.SaveDocumentAsync();
        }
    }
    finally
    {
        // 不管成功失败都要释放锁,避免死锁
        _richEditSyncLock.Release();
    }
}

3. 改造标签切换和表单关闭事件

在OnActiveTabIndexChanged和AppointmentFormClosing里,同样先等待锁释放,再执行切换或关闭逻辑:

private async Task OnActiveTabIndexChanged(int newIndex)
{
    await _richEditSyncLock.WaitAsync();
    try
    {
        ActiveIndex = newIndex;
        // 这里放你原来的标签切换逻辑
    }
    finally
    {
        _richEditSyncLock.Release();
    }
}

private async Task AppointmentFormClosing(SchedulerAppointmentFormClosingEventArgs args)
{
    await _richEditSyncLock.WaitAsync();
    try
    {
        // 表单关闭前的必要操作,比如检查并保存RichEdit内容
        if (RichEditDesc.IsModified)
        {
            await RichEditDesc.SaveDocumentAsync();
        }
        // 其他关闭逻辑
    }
    finally
    {
        _richEditSyncLock.Release();
    }
}

方案优势

  • 不管事件触发顺序如何,SemaphoreSlim都会让后触发的任务等待先触发的完成,从根源上避免RichEdit被提前释放
  • 没有固定延迟,不会导致UI卡顿,运行效率更高
  • 支持用户多次切换标签、多次触发保存的场景,每次操作都会自动同步,不需要额外重置状态

内容的提问来源于stack exchange,提问作者David Thielen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 17:25:11