2014-01-29 30 views
3

我在想,几乎所有使用ContinueWith的代码都可以用async/await重构,但这个有点难。什么我想转换草图:async/await vs ContinueWith

// 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

那么,你有什么尝试? –

+2

它不能,因为你从来没有真正“等待”那秒通过。 'ContinueWith'通常是任务中的'async' /'await'的替代品(尽管难度较大),但事实并非如此。 –

+0

@Mario,我展示了我试过的,不是吗?这不像我问“如何显示消息框并将密钥异步发送给它”,是吗? – avo

回答

5

您可以分割从中间部分变成助手异步方法:

 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}将被发送到任何控制具有焦点它关闭后,其可以导致意想不到的操作,例如在启动消息框之前在任何窗口处于激活状态时按下默认按钮。如果意图是创建一个自动消失的弹出窗口,但可以提早解雇,那么您可能只需创建一个自定义窗口来提供所需的行为。

+0

特别感谢您的注意,它确实有道理。 – avo

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"); 
+1

这实际上并不相同,因为在原始代码中,消息框会在1秒延迟过去之前出现。 –

+0

'SendKeys.Send'是同步的。要添加@DanBryant所说的内容,您正在为当前集中的控件执行SendKeys.Send,因为目前还没有消息框。 – Noseratio

1

不完全是你的代码,但你可以做这样的事情(未经测试):

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

我认为这是非常密切的。

+0

这非常接近,但它确实有效地延迟了“在消息框之后继续”,即使您在一秒钟之前关闭消息框也是如此。实际上,由于人为的反应时间限制,这不太可能出现。这将成为一个问题,但如果延迟后来增加,突然显示延迟总是被强制执行,无论盒子什么时候关闭。 –

+0

@DanBryant,同意。如何更新代码?如果消息框已经关闭,该版本还会处理不需要的'SendKeys.Send'。 – Noseratio

+1

是的,这实际上比OP的代码略好,因为如果该盒子没有真正关闭,它只会发送{ENTER}。可能仍然有一些可能的竞赛消息抽取以及消息框关闭的时间(可能仍然允许在消息框关闭后发送{ENTER}),但在此版本中它们的可能性要低得多。 –