简体   繁体   中英

async/await vs ContinueWith

I was thinking that almost any code using ContinueWith can be refactored with async/await , but this one is a bit tough. A sketch of what I want to convert:

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

How can it be done with async/await ?

You can split off the middle part into a helper async method:

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

This reveals a problem with the original code, though, which is that the continuation task is never 'awaited' (given a ContinueWith in the original context), meaning that any exceptions it throws will be unhandled. Ideally you store it in a Task variable somewhere and decide a good place to await it and handle any exceptions it throws.


One quick side note:

With either approach, if the user closes the message box before the second elapses, the {ENTER} will be sent to whatever control has focus after it closes, which may result in unintended actions, such as pressing the default button on whatever window was active before launching the message box. If the intention is to create a pop-up that goes away automatically, but can be dismissed early, you probably want to just create a custom window that provides the desired behavior.

Not exactly your code, but you can do something like this (untested):

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

I think it's pretty close.

You could abstract your task out into a method and then await on the method like...

Task DelayTask() {
    return Task.Delay(1000);
}


await DelayTask();
SendKeys.Send("{ENTER}");
MessageBox.Show("Hello!");
Debug.Print("continue after a message box");

Or you could just write it like...

await Task.Delay(1000);
SendKeys.Send("{ENTER}");
MessageBox.Show("Hello!");
Debug.Print("continue after a message box");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM