Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 741
avo Avatar asked Jan 12 '23 08:01

avo


2 Answers

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.

like image 150
Dan Bryant Avatar answered Jan 21 '23 17:01

Dan Bryant


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.

like image 30
noseratio Avatar answered Jan 21 '23 15:01

noseratio