wpf 中一个同步等待异步任务的方法

一些旧的历史项目,在维护时,想要加入异步方法,但是使用 async/await 就会向上传染,又不想改整个调用链,或者说改动涉及太多逻辑,改不了,只能在同步方法里,做异步等待,但是使用 .wait() 或 .result 会卡 UI。

为此,我们不得不使用一些骚操作,例如

var result = Task.Run(async () => await GetDataAsync()).Result;

这就使得写法非常奇怪,而且开启线程池耗性能,没有利用好异步的优势。

在github上发现一个比较优雅的解决办法:

链接:github链接

public static T WaitOnDispatcherFrame<T>(this Task<T> task)
    {
        if (!task.IsCompleted)
        {
            var frame = new DispatcherFrame();
            task.ContinueWith(static (_, s) => ((DispatcherFrame)s!).Continue = false, frame);
            Dispatcher.UIThread.PushFrame(frame);
        }

        return task.GetAwaiter().GetResult();
    }

它避免了 Task.Wait().Result 可能导致的 UI 死锁问题。非常适合在开头的需求场景。

但它是avalonia 版本的代码,在wpf中,没有Dispatcher.UIThread,因此想要在wpf中使用的话,还要稍加改造:

public static T WaitOnDispatcherFrame<T>(this Task<T> task)
{
    if (!task.IsCompleted)
    {
        var frame = new DispatcherFrame();
        task.ContinueWith(static (_, s) => ((DispatcherFrame)s!).Continue = false, frame, TaskScheduler.FromCurrentSynchronizationContext());
        Dispatcher.PushFrame(frame);
    }

    return task.GetAwaiter().GetResult();
}

下面是测试代码:

MainWindow.xaml.cs:

private void Button_Click(object sender, RoutedEventArgs e)
{
    var result = GetResultAsync().WaitOnDispatcherFrame();
    // 操作 UI 控件
    btn.Content = result.ToString();
}

public async Task<string> GetResultAsync()
{
    await Task.Delay(5000);
    return "Succeed";
}

此调用不会卡UI,并且能在异步完成后,才继续向下执行。

© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情

    暂无评论内容