一些旧的历史项目,在维护时,想要加入异步方法,但是使用 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
暂无评论内容