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

如何确保UI与非UI代码中ContinueWith和await行为等价

实现与await等价的Task.ContinueWith逻辑

要完全复刻await的行为,需要解决两个核心问题:

  1. 安全获取当前上下文对应的TaskScheduler,避免无有效同步上下文时抛出异常
  2. 正确传播原任务的异常和取消状态,和await的异常处理逻辑对齐

问题分析

你原来的代码存在两个关键缺陷:

  • 直接调用TaskScheduler.FromCurrentSynchronizationContext()时,若当前无有效同步上下文(如Mono WASM无UI场景),会抛出InvalidOperationException
  • 未处理原任务的异常/取消状态,导致原任务出错时异常被静默吞掉,和await的异常传播行为不一致

解决方案

1. 安全获取当前调度器

先尝试从当前同步上下文创建调度器,若失败则回退到当前TaskScheduler(无特殊调度器时使用线程池默认调度器):

using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;

TaskScheduler GetSafeCurrentScheduler()
{
    try
    {
        var syncContext = SynchronizationContext.Current;
        if (syncContext != null)
        {
            return TaskScheduler.FromCurrentSynchronizationContext();
        }
    }
    catch (InvalidOperationException)
    {
        // 捕获上下文无法转换为TaskScheduler的异常(如部分非UI同步上下文)
    }
    
    // 无有效同步上下文时,使用当前TaskScheduler,默认回退到线程池调度器
    return TaskScheduler.Current;
}

2. 带异常处理的ContinueWith逻辑

使用上述安全获取的调度器,并在延续委托中处理原任务的异常和取消状态,确保和await的行为完全一致:

DoSomethingAsync().ContinueWith(
    t =>
    {
        // 复刻await的异常传播逻辑
        if (t.IsFaulted)
        {
            // 保留原始异常的调用栈
            ExceptionDispatchInfo.Capture(t.Exception.InnerException).Throw();
        }
        else if (t.IsCanceled)
        {
            throw new TaskCanceledException(t);
        }
        
        // 执行延续操作
        DoSomethingElse();
    },
    GetSafeCurrentScheduler());

行为说明

  • 当存在有效UI同步上下文(如WPF的DispatcherSynchronizationContext)时,延续操作DoSomethingElse()会在原UI线程执行
  • 当无有效同步上下文时,延续操作会在当前TaskScheduler对应的线程执行(无特殊调度器时默认使用线程池线程)
  • 原任务的异常和取消状态会被正确传播,和await的行为完全一致

内容的提问来源于stack exchange,提问作者Emperor Eto

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 21:13:19