async / await vs ContinueWith

时间:2014-01-29 14:20:42

标签: c# winforms task-parallel-library async-await

我在想,几乎所有使用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完成?

3 个答案:

答案 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");