考虑一个返回带有某些值的任务的 API。
我想基于这些值并行更新 UI(当其中一个值准备好时,我想更新它而不等待第二个值,假设每个值的更新都是它自己的更新方法)。
public async Task MyFunc()
{
Task<First> firstTask = MyAPI.GetFirstValue();
Task<Second> secondTask = MyAPI.GetSecondValue();
UpdateFirstValueUI(await firstTask)
UpdateSecondValueUI(await secondTask)
}
该代码示例将等待第一个值,更新 UI,等待第二个值并再次更新 UI。
该场景的最佳实践是什么?我想知道是否ContinueWith
是最佳实践,因为我主要在遗留代码中看到它(在有async
-之前await
)。
编辑一个更好的例子:假设我们有该 API 的两个实现,代码看起来像这样
public async Task MyFunc()
{
Task<First> firstTask = null
Task<Second> secondTask = null
if (someCondition)
{
firstTask = MyAPI1.GetFirstValue();
secondTask = MyAPI1.GetSecondValue();
}
else
{
firstTask = MyAPI2.GetFirstValue();
secondTask = MyAPI2.GetSecondValue();
}
UpdateFirstValueUI(await firstTask)
UpdateSecondValueUI(await secondTask)
}
现在如您所见,我不想在两个不同的分支中调用更新方法(假设我们在分支后为每个 API 拆分该方法),因此正在寻找一种仅更改更新调用的方法,以便它们可以并行发生
这ContinueWith
是一种在库代码中很少使用的原始方法,通常应避免在应用程序代码中使用。在主要使用的问题,ContinueWith
你的情况是,它要执行的延续上ThreadPool
,这是不是你想要的,因为你的目的是更新UI。从 UI 线程以外的任何其他线程更新 UI是不可以的。可以通过使用合适的 配置来解决这个¹ 问题,但是使用 async/await 组合来解决它要简单得多。我的建议是在项目的某个静态类中添加以下方法:ContinueWith
TaskScheduler
Run
public static class UF // Useful Functions
{
public static async Task Run(Func<Task> action) => await action();
}
此方法仅调用并等待提供的异步委托。您可以使用此方法将异步 API 调用与其 UI 更新延续相结合,如下所示:
public async Task MyFunc()
{
Task<First> task1;
Task<Second> task2;
if (someCondition)
{
task1 = MyAPI1.GetFirstValueAsync();
task2 = MyAPI1.GetSecondValueAsync();
}
else
{
task1 = MyAPI2.GetFirstValueAsync();
task2 = MyAPI2.GetSecondValueAsync();
}
Task compositeTask1 = UF.Run(async () => UpdateFirstValueUI(await task1));
Task compositeTask2 = UF.Run(async () => UpdateSecondValueUI(await task2));
await Task.WhenAll(compositeTask1, compositeTask2);
}
这将确保在每个异步操作完成后立即更新 UI。
作为旁注,如果您怀疑MyAPI
异步方法可能包含阻塞代码,您可以ThreadPool
使用该Task.Run
方法将它们卸载到,如下所示:
task1 = Task.Run(() => MyAPI1.GetFirstValueAsync());
有关为什么这是一个好主意的详尽解释,您可以查看此答案。
内置Task.Run
方法和UF.Run
上面介绍的自定义方法之间的区别在于,在 上Task.Run
调用异步委托ThreadPool
,而UF.Run
在当前线程上调用它。如果您对比 更好的名称有任何想法Run
,请提出建议。:-)
¹这也ContinueWith
带来了许多其他问题,例如在AggregateException
s 中包装错误,很容易误吞异常,难以传播IsCanceled
先行任务的状态,使得泄漏即发即忘任务变得微不足道,需要由异步委托等创建的Unwrap
嵌套Task<Task>
s。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句