@SelAromDotNet : You posted this in May - sorry for the late reply, I just saw this.
First, read this excellent blog post by Stephen Cleary titled "There is no Thread" - it explains that when an async method awaits a method that performs an async I/O operation, there is no thread handling the wait. In your controller action, when the thread that is processing your request within the IIS request pipeline comes to the instructions to await an async web request, it signals the machine's NIC device driver to make a request to the external service, and then the thread is released to handle processing other requests in IIS. When the request to the external service completes, the NIC driver signals (via a bunch of low level events) that the request has completed, and IIS assigns a thread pool thread to the continuation to finish the processing of your controller's action method. This means that the entire time the HTTP request to the external service is pending, the thread that was initially processing your request is free to perform other work.
So the options that you've laid out:
1) Use WebClient to make "synchronous" HTTP requests - This is effectively the same thing as blocking on an async HttpClient request. In both instances the underlying request is being performed asynchronously by the device driver, but the .NET code issuing that request is blocking the thread while waiting for a response. This means that thread is tied up, doing nothing, until the request to the external service completes.
This is what the work around I posted in the initial post is doing. Note that either of these methods synchronously execute async code, and both block the thread while doing so:
// Or this does the same thing, but thrown exceptions don't get wrapped in an AggregateException...
Task.Run(() => IndexAsync())
async Task<ActionResult> IndexAsync()...
This work around, blocking on async code, can cause the execution of your request's processing to deadlock. See Don't Block on Async Code
For information about .Result vs. Task.Run(...).GetAwaiter().GetResult() see A Tour of Task, Part 6: Results
2) You could use classic MVC, but then why are you even using Sitefinity?
3) This is probably your best bet, you get to keep Sitefinity's widget functionality and you aren't blocking while synchronously executing async code. If this is possible, by all means, go for it.
Are there any other strategies for handling async actions in SF? Nope, those three are the three options you have: 1) use SF and execute async methods synchronously, blocking the thread handling the request; 2) use Classic mode which bypasses Sitefinity's routing and view engine - essentially a non-SF application with the SF backend available via the SF APIs; 3) use SF, but only non-async actions, and make AJAX requests to services hosted outside of SF that properly use async actions.
Unfortunately, none of those solve the problem of proper handling of async actions on controllers in Sitefinity. They're all just ways to work around the problem.