Async/Await With/Without Awaiting (Fire and Forget)

Async/await with/without awaiting (fire and forget)

I'm asking this, because we're moving our app to service fabric where we no longer can use HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => await LongMethodAsync()); and the advice is to simply replace it with Task.Run.

That's bad advice. You should use a separate background process separated from your web frontend by a queue.

What's the in-depth logic behind the calls?

  1. Starts the asynchronous method on the current thread. Ignores all results (including exceptions).
  2. Starts the asynchronous method on the current thread. Asynchronously waits for it to complete. This is the standard way of calling asynchronous code.
  3. Starts the asynchronous method on a thread pool thread. Ignores all results (including exceptions).
  4. Starts the asynchronous method on a thread pool thread. Asynchronously waits for it to complete.
  5. Exactly the same as #3.
  6. Exactly the same as #4.

Fire and forget, using `Task.Run` or just calling an async method without `await`

In general especially when it comes to libraries or console apps, in order to fire and forget an async method, is it better to just call the async method without awaiting it or use Task.Run?

In general, it's best not to use fire-and-forget at all.

"Fire and forget" means:

  1. You don't care if the code has an exception. Any exceptions will cause it to fail silently; no logging, no notification, etc.
  2. You don't need to know when the code completes. I.e., the consuming application doesn't ever need to wait for the code to complete. Even during shutdown.
  3. You don't need the code to complete. As a corollary of (2), fire-and-forget code may not run to completion; and as a corollary of (1), you would have no notification that it failed to complete.

In short, "fire and forget" is only appropriate for an extremely small number of tasks. E.g., updating a cache. I'd say probably 85% or more of "fire and forget" code is wrong - it's using fire and forget for code that should not be fire and forget.

So I'd say the best solution is to not use fire and forget at all. At the very least, you should expose a Task somewhere that represents a "followup action". Consider adding the Task to your return type or exposing it as a property.

Adopting fire and forget - especially in a library - means you're forcing all consumers to never know when it's safe to shut down and exit. But if you really want to do fire and forget, there are a few options.

A. One option is calling an async void function without a context. The consuming application still has no way to determine if/when the code completes, but at least that way exceptions are not ignored.

B. Another option is to start the task without a context. This option has both disadvantages of fire and forget code: exceptions are ignored and the calling code cannot know when it completes.

Both of these recommendations start the task without a context. There are helpers for doing this, or you can wrap the call in Task.Run (slightly less efficient, but it works fine).

I wouldn't recommend starting the task directly. While this would work fine in a Console app, it's not appropriate for libraries which may be called in situations where a context is provided.

Fire and Forget Async Await Call

It's just a warning, so that you know it's not been awaited on. You can use discard to make it clear you don't care whether or not it ends up running to completion:

_ = SendSMS();

So, question is, If the UserRegistration() method returns before completion of SendSMS() OR SendEmail(), Does it fire the exception?

Nope.

And Is it good practice to make Fire and Forget call in Async Await Methods?

Not in my opinion.

Non awaitable as fire & forget - is it safe to use?

I don't to use Task.Run for this kind of operation, because it's bad.

It's important to recognize/specify context for this kind of statement. Task.Run is bad on ASP.NET. It's perfectly OK to use in a GUI app on the client side.

We need to write some logs in our code.

I strongly recommend using an established logging library. Most of them work by using an in-memory queue that is (synchronously) written to by your code, and which is continually processed by a background thread. This is a pattern that is well-established.

Is it a viable solution for logging in a "fire-and-forget" mode?

One of the problems with "fire and forget" is that you don't know when there are errors. The established logging libraries all have some kind of system for dealing with errors communicating with the logging backend; the code you posted will just ignore them.

What if I create objects inside this inline method, when are they going to be GC'ed?

Conceptually, async methods act as GC "roots" as long as they are going to continue executing in the future. So local objects will be GC'ed when the method completes.

Fire-and-forget with async vs old async delegate

Avoid async void. It has tricky semantics around error handling; I know some people call it "fire and forget" but I usually use the phrase "fire and crash".

The question is: Given a synchronous method A(), how can I call it asynchronously using async/await in a fire-and-forget manner without getting a solution that is more complicated than the "old way"

You don't need async / await. Just call it like this:

Task.Run(A);

Using await on async method that returns only Task

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.

Run a async function without waiting for the response in c# webforms (fire and forget)

How do I make this truly async?

In ASP.NET, await yields to the thread pool, not the client.

If possible, I recommend changing the client so that a longer request is handled appropriately; e.g., calling this via JavaScript instead of a click handler, and updating the page when it completes.

If you really need to return early from an web request, then you should implement a basic distributed architecture. As described on my blog, this consists of two parts:

  • A durable queue of work.
  • A backend processor of that queue.

Should async method always call await, if not what is the implication?

Should async method always call await, if not what is the implication?

As I describe on my blog, async methods always begin executing synchronously, just like any other methods. await is where things may get asynchronous.

If a code path is taken where there is no await (or if the tasks awaited are already completed), then the method completes synchronously and returns an already-completed task to its caller. This is not a problem in practice, because "*Async" means "may be asynchronous", not "must be asynchronous".

In this specific example, you're using async void, but if this was an async Task method that was called a lot, in that case I'd recommend considering returning ValueTask instead, which would save some memory allocation for the Task whenever it completes synchronously.



Related Topics



Leave a reply



Submit