0

In my ASP.NET Core app I have few (dozen) methods created in the form of

public async Task DoStuff() 
{ 
   // ...
}

So, no return type.

Later, I realized that I accidentally forgot to include await on such methods here and there, in the caller methods (which were obviously left without async keyword as async "zombie virus" hasn't spread out that far yet).

During the execution, there were no unwanted consequences whatsoever.

The thing is, Visual Studio generates no warning message either when such thing happens and I'm asking myself, is there actually any danger present of lefting out the await in such cases? I know that await should be applied on every async method naturally, but I don't really understand the reasons behind this when caller practically has no return value to use. Perhaps something with catching exceptions?

I haven't found any clear answer to this for the general statement is 'simply include await'. And believe it or not, this async/await thing, to which I'm relatively new, keeps biting me repeatedly from time to time.

6
  • 2
    So, no return type - no, the return type is Task, as opposed to e.g. async void. Commented Feb 7, 2021 at 12:08
  • 1
    Does this answer your question? C# async/await with/without awaiting (fire and forget) Commented Feb 7, 2021 at 12:09
  • @GSerg sorry, I did meant in the context of async/await terminology, as no return type is produced by the method to by wrapped by the Task. Should work on my wording, I guess. Commented Feb 7, 2021 at 12:22
  • @GSerg thanks for the suggestion, I see there that Stephen partially answered my question, by looking at the explanation to #1 provided. Having this in mind, can you perhaps comment the following: if I handle the exceptions in the said methods themselves (the ones not properly awaited), could we then say that all is fine and well? Caller won't know about the exceptions, yes, but they won't be entirely lost. Commented Feb 7, 2021 at 12:28
  • Is it possible to include in the question a more realistic example than the DoStuff method? It would be easier to answer the question if this method had a name that indicates what it does. Also by showing how and where you call it would help us estimate whether omitting the await has consequences, and what kind of consequences. Btw according to the guidelines, the DoStuff method should have the name DoStuffAsync. Commented Feb 7, 2021 at 12:56

1 Answer 1

5

During the execution, there were no unwanted consequences whatsoever.

I disagree. The resulting code is dangerous. ASP.NET pre-Core was able to detect similar situations and throw an exception ("An asynchronous module or handler completed while an asynchronous operation was still pending"). For technical reasons, ASP.NET Core cannot detect this situation so you don't get that "safety net" exception, but the situation itself is still just as bad.

The thing is, Visual Studio generates no warning message either when such thing happens

You don't get CS4014 ("Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the await operator to the result of the call.")?

is there actually any danger present of lefting out the await in such cases? I know that await should be applied on every async method naturally, but I don't really understand the reasons behind this when caller practically has no return value to use. Perhaps something with catching exceptions?

Yes, there are dangers. Task (even without a result type) is used for two things: for the caller to know when the operation completes, and for the caller to detect exceptions from that operation.

So, the one issue is that exceptions are silently swallowed. More specifically, exceptions from the async method are captured by the async state machine and placed on the returned Task, which is then ignored.

if I handle the exceptions in the said methods themselves (the ones not properly awaited), could we then say that all is fine and well?

No, because the other issue still exists: the caller doesn't know when the asynchronous operation completes. This is particularly important to know in ASP.NET, because the result should not be sent until the operation is complete. Any kind of "fire and forget" code on ASP.NET lives outside the request/response lifetime; i.e., it's request-extrinsic code.

I go into some detail on my blog about why request-extrinsic code is dangerous. In summary, your ASP.NET handler may complete too soon, and in that case, the request-extrinsic code may get "lost". At the very least, whatever it's doing won't be done by the time the response is sent; and in the case of a regular shutdown (e.g., rolling upgrades), it might not get done at all.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for the answer, Stephen. I traced down all the cases where await is missing. As I said, there were no warning as those asyncs that return only plain Task were called from the sync methods. Still, I have one case where I'm unable to await the async called - this are the methods of an WCF IClientMessageInspector, and they contain a 'ref' as a parameter e.g. void AfterReceiveReply(ref Message reply, object correlationState). The async called is not something bussines-critical (it's a logger) but still, do you propose any workaround to this?
@NamespaceNotFound: For loggers, I recommend having a separate dedicated logging thread, and make all the logging methods synchronously add to an in-memory queue that is processed by that thread. So that way, logging doesn't require asynchrony.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.