我在想,几乎所有使用ContinueWith
的代码都可以用async/await
重构,但这个代码有点难度。我要转换的草图:
// UI unit testing: close the message box in 1s
Task.Delay(1000).ContinueWith((t) =>
{
SendKeys.Send("{ENTER}");
}, TaskScheduler.FromCurrentSynchronizationContext());
MessageBox.Show("Hello!");
Debug.Print("continue after a message box");
如何使用async/await
完成?
答案 0 :(得分:7)
您可以将中间部分拆分为辅助异步方法:
private async Task EnterAfterDelay()
{
await Task.Delay(1000);
SendKeys.Send("{ENTER}");
}
private void MyMethod()
{
EnterAfterDelay();
MessageBox.Show("Hello!");
Debug.Print("continue after a message box");
}
这揭示了原始代码的问题,即继续任务永远不会被“等待”(在原始上下文中给出一个ContinueWith),这意味着它抛出的任何异常都将是未处理的。理想情况下,你将它存储在某个地方的Task变量中,并决定一个好的地方等待它并处理它抛出的任何异常。
一个简短的说明:
使用任何一种方法,如果用户在第二次过去之前关闭消息框,{ENTER}将被发送到关闭后具有焦点的任何控件,这可能导致意外的操作,例如按下任何内容的默认按钮窗口在启动消息框之前处于活动状态。如果打算创建一个自动消失但可以提前解散的弹出窗口,您可能只想创建一个提供所需行为的自定义窗口。
答案 1 :(得分:2)
不完全是您的代码,但您可以执行此类操作(未经测试):
Func<Action, Task> doAsync = async (action) =>
{
await Task.Yield();
action();
};
var messageBoxTask = doAsync(() =>
MessageBox.Show("Hello!"));
var delay = Task.Delay(1000);
var task = await Task.WhenAny(delay, messageBoxTask);
if (task != messageBoxTask)
{
SendKeys.Send("{ENTER}")
await messageBoxTask;
}
Debug.Print("continue after a message box");
我认为它非常接近。
答案 2 :(得分:0)
您可以将任务抽象为方法,然后等待方法,如...
Task DelayTask() {
return Task.Delay(1000);
}
await DelayTask();
SendKeys.Send("{ENTER}");
MessageBox.Show("Hello!");
Debug.Print("continue after a message box");
或者你可以把它写成......
await Task.Delay(1000);
SendKeys.Send("{ENTER}");
MessageBox.Show("Hello!");
Debug.Print("continue after a message box");