I've searched for the answer to this but according to many guides and SO questions this code still appears correct to me, yet it runs synchronously.
private void CheckConditions()
{
    foreach (var obj in myObjects)
    {
        if (obj.ConditionMet)
        {
            HandleConditionAsync(obj);
        }
    }
    DoOtherWork();
}
private async void HandleConditionAsync(MyObject obj)
{
    // shouldn't control transfer back to CheckConditions() here while we wait for user input?
    string userInput = await obj.MessagePromptAsync("hello user");
    DoSomeBookkeeping(obj);
}
// (MyObject.cs)
private MessagePrompt messagePrompt; // inherits from UserControl
public async Task<string> MessagePromptAsync(string prompt)
{
    return await Task.FromResult<string>(messagePrompt.Prompt(prompt));
}
// (MessagePrompt.cs)
public string Prompt(string prompt)
{
    this.UIThread(() => this.SetMessagePrompt(prompt));
    userInputAutoResetEvent.WaitOne();
    return myResult; // set in a button handler that also sets the AutoResetEvent
}
I'm intending for CheckConditions() to continue along merrily but instead it is stuck on MessagePrompt's AutoResetEvent despite my async/awaits. The only thing I can figure might be wrong is that perhaps MessagePrompt's methods aren't able to run asynchronously due to some restriction from UserControl, its use of a UI thread reference, or maybe non-async methods at the top of the stack.
 
     
     
    