Safely getting a result from a async method within a sync method
I have an async method that returns a response object. In the calling sync method, what is the best way to get the result and capture any possible exceptions?
17 Replies
afaik there is always the possibility of screwing up (ergo deadlock) when using sync over async, and there is no real solution
Async functions are infectious, so there is a solution: make your synchronous function async as well.
The other poster is partially correct with the deadlock danger. Depending on how your sync method is called, you run the risk of a deadlock if you synchronously (blocking) await the task. So if you see ai or other peopls reccommend things like
AsyncMethod().Result
, safely disregard that and try to make the calling method async instead, so you may properly await
the task.wrapping the AsyncMethod in a Task.Run could be less dangerous than a direct .Result/.Wait();
if you can describe what is the general architecture of your software maybe someone can come up with a better advice
The best advice in most cases is to make your calling method async.
See here : https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
for an explanatikn
Don't Block on Async Code
This is a problem that is brought up repeatedly on the forums and Stack Overflow. I think it’s the most-asked question by async newcomers once they’ve learned the basics.
Using
async void
puts in a massive risk of breaking your application when there's an uncaught exception. Please wrap code in a try-catch if you really want to do that
Assuming that method doesn't return a response, you could make it async void
. If it does return a response, then the parent method above that might also have to be async
But this is a lot of code, and there's one thing you could consider: If this method runs once and not in parallel, then you could also just make AsyncMethod
fully synchronous and just have it block the main thread for a period
People often suggest to make everything async, but often code is simply too synchronous. In this case you might just want to block the thread when there's nothing else that rely on it. This obviously does cause issues when you work with a web api, for example. But making async work in some cases might just not be worth it since you end up calling it synchronously from the root anyway
So in this case, if you can make the root truly asynchronous, go ahead and just make everything async. However, if SyncMethod
can't be async because it returns a synchronous response and the project in general can handle blocking, then just wait for .Result
If it doesn't want a response, then by all means make it async void
as long as you handle exceptions
What you can also do is make AsyncMethod
async void
in a similar manner, and store the response in a class-scoped field/property
Then you can have some other method use the result when it exists, either by having AsyncMethod
call a method when it is done, or by keeping AsyncMethod
a Task
and using ContinueWith
If you want my 2 cents, then use ContinueWith
make it async voidThere is absolutely no need for this in the code OP shared; this is genuinely bad advice. Unless you are writing event handlers for a UI framework dictating a void returning handler, you should not use
async void
.Have you read the rest of my message? I doubt you have
Also
async void
is not bad advice, it's just risky if not handled correctly
The answer here depends entirely on context and whether or not a result is being awaited for. async void
is one of many solutionsWhy use
async void
(risky) over async Task
(not risky)?
I'd ask you kindly not to assume what I read please. I did read your message but none of what OP presented requires async void
.
If they want to synchronously block await on a task, then .Result
will do (caveats like deadlock risks apply, of course), if they don`t, then just throw away the task.This makes literally no difference when the parent method remains synchronous
At best you discard the task and end up with the same behaviour
Now to repeat my first message:
Please wrap code in a try-catch if you intend to call an async method from a synchronous method. This way you ensure you don't leave uncaught dangling exceptions that put a massive risk of breaking your application. This article also specifically mentions the usefulness of Tasks when you must await a result.
Unless it's
async void
. You don't have to wait for the result of a method that you don't care about. You can fire-and-forget the method, as long as you properly handle any exceptions that happen.
But anyway, it doesn't really matter here. OP needs a response object so the best way is to either block the response or use the Task
ContinueWith
method, which I specifically also pointed out as a better solutionOp specified
capture any possible exceptions
async void
does not do that, async Task
does. There is no relevant upside to using async void
over async Task
for this.
ContinueWith
might as well be implemented using async/awaitasync void does not do that, async Task does. There is no relevant upside to using async void over async Task for this.Yes, I know, that's why I specifically mentioned that it doesn't matter here since they are waiting for a response
ContinueWith might as well be implemented using async/awaitAgain, you don't know that. If the method itself is required to be synchronous and it's not possible to just block a thread like that, then you can't use
.Result
or refactor the parent to be async
The answer here depends entirely on context
This I agree with.
I simply listed all possibilities on what OP might use in this situation, making sure that they are aware why you would use one over the other
I'd like to know why the method needs to be synchronous @Wrenpo
Sorry, was busy at work and didn't get back to this. Thank you for all the responses!
I am aware of the deadlocking possibilities and have read up on some of Stephen Cleary's articles about this including his SO answers to similar questions. I have since converted my async method into a synchronous one. I am running into a situation now where the remote server is closing the request early. I am working with Azure AD B2C and .NET Framwork 4.8. Inside our middleware (OWIN), a synchronous method is establishing the initial connection with Azure AD B2C to identify a user and allow them into the app. However, what this does not handle is giving back the refresh token. It was assumed that it did this automatically. It does not. It disposes it and does not store it anywhere. So, I have been manually trying to do this from what was originally my async method. I can take my authorization code that I get from B2C and with a request body, send a GET over to the token endpoint on Postman and get what I want. I am going a bit crazy trying to get this working from the app.
good luck i guess