How do I call an async method from a synchronous one?
Writing code to be async when possible is great, but not always possible. So how do call async code from legacy, synchronous code?
Running as much of your code at the same time (asynchronously) as you can is great, if and when you can do it. It's not always an option though, especially when you're working in legacy codebases that were written well before any of this was relatively easy to implement.
(FYI, the code in this post is available here.)
Say you implement a new method using async, mark the method appropriately, call some other async methods from within it, and then... wait. How do you call it from your legacy (and very-synchronous) code?
private async Task MyShinyNewAsyncMethod()
{
await Task.WhenAll(
CallSomeOtherAsyncMethod(),
AndYetAnotherAsyncMethod(),
WhyNotOneMoreAsyncMethod()
);
}
private async Task CallSomeOtherAsyncMethod()
{
await Task.Run(() => Thread.Sleep(2000)});
}
private async Task AndYetAnotherAsyncMethod()
{
await Task.Run(() => Thread.Sleep(3000));
}
private async Task WhyNotOneMoreAsyncMethod()
{
await Task.Run(() => Thread.Sleep(1000));
}
Most of the resources I find about how to use an async method encourage you to mark everything calling it as async too, and they should. The problem is that many of them don't address the elephant in the room - what if you can't mark everything async?
Sure, with enough time you could, but few companies with a legacy codebase are going to happily let us spend days on a task that should've taken a few hours, just because we decided to make the code "better". That's a tough sell any day of the week.
If you happen to be calling an async method from an event like a button click, you can try marking the event method as async. That should work okay, as long as you remember that control will return to the user before the async code finishes.
private async void btnExample1_Click(object sender, EventArgs e)
{
btnExample1.Enabled = false;
await MyShinyNewAsyncMethod();
btnExample1.Enabled = true;
}
In other situations though, you might have a synchronous method that you just can't change right now. Full stop, draw a line in the sand, thou shall not pass. What then?
private void btnExample2_Click(object sender, EventArgs e)
{
MySynchronousHillToDieOn();
}
private void MySynchronousHillToDieOn()
{
MyShinyNewAsyncMethod();
}
If you just call the async method without waiting, the main UI thread returns control to the user before the async code completes, which might be what you want.. or it might not be. And if you just slap an await
on there, you get yelled at.


The third option is to run the async code in a separate task and wait on that. You can do that with two lines like in the method below.
private void btnExample3_Click(object sender, EventArgs e)
{
btnExample3.Enabled = false;
MySynchronousHillToDieOn_Wait();
btnExample3.Enabled = true;
}
private void MySynchronousHillToDieOn_Wait()
{
var task = Task.Run(async () => await MyShinyNewAsyncMethod(progress));
task.Wait();
}
And if you need to collect the value to be returned, then swap out the call to .Wait()
with .Result
.
private void btnExample4_Click(object sender, EventArgs e)
{
btnExample4.Enabled = false;
MySynchronousHillToDieOn_WaitForReturnValue();
btnExample4.Enabled = true;
}
private void MySynchronousHillToDieOn_WaitForReturnValue()
{
var progress = new Progress<int>(thread =>
Controls.Cast<Control>().Single(x => x.Name == $"lblThread{thread}").Text = "Done!");
var t = Task.Run(async () => await MyShinyNewAsyncMethodThatReturnsAValue(progress));
lblReturnValue.Text = t.Result.ToString();
}
private async Task<DateTime> MyShinyNewAsyncMethodThatReturnsAValue(IProgress<int> progress)
{
await MyShinyNewAsyncMethod(progress);
return DateTime.Now;
}
I threw together a small WinForms app showing each of the above examples. Check it out below or grab the code and play around with it yourself.

One caveat to mention is that since you're creating another thread to run the task, you'll exhaust the available threads faster. I don't think that'll typically be a problem in a WinForms app, but I suppose it depends on your exact situation.. and other environments like web servers might be more problematic.
If you want to read more about multithreading and async code, check out these related posts too.
Comments / Reactions