您可以将对RunAsync 的调用包装在您自己的异步方法中,该方法可以等待并控制任务的完成,从而自己继续等待调用者。
由于 async-await 以 Task 类型为中心,因此您必须使用该类型来编排工作。但是,通常Task 将自己安排在线程池线程上运行,因此不能用于安排 UI 工作。
然而,TaskCompletionSource 类型的发明是为了充当不定期Task 的傀儡。换句话说,TaskCompletionSource 可以创建一个虚拟的Task,它没有计划做任何事情,但通过TaskCompletionSource 上的方法可以看起来像正常工作一样运行和完成。
看这个例子。
public Task PlayDemoAsync()
{
var completionSource = new TaskCompletionSource<bool>();
this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
try
{
foreach (var ppc in this.Plots.Select(p => this.TransformPlot(p, this.RenderSize)))
{
// For each subsequent stroke plot, we need to start a new figure.
//
if (this.Sketch.DrawingPoints.Any())
this.Sketch.StartNewFigure(ppc.First().Position);
foreach (var point in ppc)
{
await Task.Delay(100);
this.Sketch.DrawingPoints.Add(point.Position);
}
}
completionSource.SetResult(true);
}
catch (Exception e)
{
completionSource.SetException(e);
}
});
return (Task)completionSource.Task;
}
注意:UI 线程上的主要工作只是每 100 毫秒在屏幕上绘制一些线条。
TaskCompletionSource 被创建为 puppet master。查看接近尾声,您会看到它有一个Task 属性返回给调用者。返回Task 可以满足编译器的需求,并使方法可等待和异步。
但是,Task 只是一个傀儡,是 UI 线程中实际工作的代理。
看看我如何在主 UI 委托中使用 TaskCompletionSource.SetResult 方法将结果强制到 Task(因为返回给调用者)并传达工作已完成。
如果出现错误,我会使用 SetException 来“拉另一个字符串”,并让它看起来像是在 puppet Task 中冒出异常。
async-await 子系统没有什么不同,因此它可以按您的预期工作。
编辑
根据 svick 的提示,如果该方法设计为只能从 UI 线程调用,那么这就足够了:
/// <summary>
/// Begins a demonstration drawing of the asterism.
/// </summary>
public async Task PlayDemoAsync()
{
if (this.Sketch != null)
{
foreach (var ppc in this.Plots.Select(p => this.TransformPlot(p, this.RenderSize)))
{
// For each subsequent stroke plot, we need to start a new figure.
//
if (this.Sketch.DrawingPoints.Any())
this.Sketch.StartNewFigure(ppc.First().Position);
foreach (var point in ppc)
{
await Task.Delay(100);
this.Sketch.DrawingPoints.Add(point.Position);
}
}
}
}