I read that returning void from a C# async call is not good. But I have the following scenario:
public async void MainFunction()
{
await DoSomething()
await DoSomethingMore()
}
public void DoSomething()
{
//some code that I want to execute (fire and forget)
}
public void DoSomethingMore()
{
//some code that I want to execute (fire and forget)
}
Since I just want that function be executed with no return at all. Should i keep it like this, or should I return Task from DoSomething()? If I change it to return Task, since my code doesn't need to return anything at all, what should I return?
If i change it to return Task, since my code need return nothing at
all, what should i return?
Your current code wouldn't compile, as you can't await on a void returning method (because it doesn't return an awaitable, which is a type which exposes a GetAwaiter method).
Any void method in the synchronous world should return a Task in the asynchronous world. This allows for anyone who wishes to asynchronously wait on the async operation, and also gets a chance to handle exceptions properly. using async void means that the exception will either be swallowed, or if set to in the configuration, will be rethrown on an arbitrary threadpool thread. Async should be propagated properly all the way.
Note that when you await both operations in MainFunctionAsync, you're not firing and forgetting, you're asynchronously waiting for each of them to complete, sequentially.
public async Task MainFunctionAsync()
{
await DoSomethingAsync();
await DoSomethingMoreAsync();
}
public Task DoSomethingAsync()
{
// Do meaningful async stuff
}
public Task DoSomethingMoreAsync()
{
// Do more meaningful async stuff
}
you can return either of these
Task.FromCompleted;
Task.FromException(ex);
so your method would be like this:
public void MainFunction()
{
await DoSomething()
await DoSomethingMore()
}
public Task DoSomething()
{
try{
//some code that I want to execute (fire and forget)
return Task.FromCompleted;
}
catch(Exception ex){
return Task.FromException(ex);
}
}
//some code that I want to execute (fire and forget)
}
public Task DoSomethingMore()
{
try{
//some code that I want to execute (fire and forget)
return Task.FromCompleted;
}
catch(Exception ex){
return Task.FromException(ex);
}
}
Related
I'm wondering how I can let this code fall in the catch of PassThrough?
using System;
using System.Threading.Tasks;
public class Program
{
public static async Task Main(string[] args)
{
try
{
await PassThrough(Test());
} catch (Exception) {
Console.WriteLine("caught at invocation");
}
Console.ReadLine();
}
public static async Task PassThrough(Task<bool> test)
{
try
{
var result = await test.ConfigureAwait(false);
// still need to do something with result here...
}
catch
{
Console.WriteLine("never caught... :(");
}
}
/// external code!
public static Task<bool> Test()
{
throw new Exception("something bad");
// do other async stuff here
// ...
return Task.FromResult(true);
}
}
fiddle
The external code should return handle the error path and return Task.FromException? Pass a Func<Task<bool>>?
My recommendation would be to change your PassThrough method to take a Func<Task<bool>> instead of a Task<bool>. This way, you can capture exceptions arising both from the synchronous part of your Test method, as well as the asynchronous task it launches. An added advantage is that asynchronous methods (defined using async and await) can be directly cast to Func<Task> or Func<Task<TResult>>.
using System;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
try
{
await PassThrough(Test);
// Note that we are now passing in a function delegate for Test,
// equivalent to () => Test(), not its result.
}
catch (Exception)
{
Console.WriteLine("caught at invocation");
}
Console.ReadLine();
}
public static async Task PassThrough(Func<Task<bool>> test)
{
try
{
var task = test(); // exception thrown here
var result = await task.ConfigureAwait(false);
// still need to do something with result here...
}
catch
{
Console.WriteLine("caught in PassThrough");
}
}
/// external code!
public static Task<bool> Test()
{
throw new Exception("something bad");
// do other async stuff here
// ...
return Task.FromResult(true);
}
}
Adding to Douglas's answer.
Only catch exceptions if you are able to do something meaningful with them and you can manage them at that level.
Task.FromException basically just places the exception on a task which you would usually return. However, in this case the Async Await Pattern already does this for you. i.e If you just let it fail, the exception will get placed on the task anyway, so there seems no real reason from your code to catch anything.
The only pertinent place you have to think about catching exceptions is in async void as they run unobserved and can cause issues when an exception is thrown
In the following line you are awaiting the PassThrough, not the Test.
await PassThrough(Test());
You could await both if you wanted:
await PassThrough(await Test()); // also need to change the signature of PassThrough from Task<bool> to bool.
...but in both cases the Test will be invoked first. And since it throws an exception, the PassThrough will never be invoked. This is the reason you don't see the "caught in PassThrough" message. The execution never enters this method.
I have some code in here. This is simplified version of a real class:
public class Delayer
{
//it has to be unawaitable
public async void Execute(Action action)
{
await Task.Delay(10).ConfigureAwait(false);
action.BeginInvoke(null, null); //action.Invoke();
}
}
I use it:
private static Task TestFoo()
{
throw new Exception();
}
delayer.Execute(async () =>
{
//do something else
await TestFoo().ConfigureAwait(false);
});
I can't hadle this exception by passing Execute method into try/catch and I can't do it by passing action.BeginInvoke(null, null) into try/catch as well. I can handle it if only I surround async lambda with try/catch when pass it to Execute method.
My question is: why is async lambda executed with await? Because if it weren't executed with await, exception would be swallowed.
I want Execute method to swallow all exceptions thrown from an action. Any ideas how to do it? What do I do wrong?
Addition:
The behavior of Execute must be like "just a fire and forget operation".
Edit
If your really, really want a Fire and Forget method the only thing to do is to
catch all exceptions in the Execute method. But you have to accept an awaitable task if you want to be able to catch exceptions instead of using BeginInvoke on a non-awaitable Action.
public class Delayer
{
public async Task Execute(Func<Task> action) // or public async void Execute(Func<Task> action) if you insist on it.
{
try
{
await Task.Delay(10).ConfigureAwait(false);
await action.Invoke();
}
catch(Exception ex)
{
Console.WriteLine(ex);
}
}
}
you can then safely do
void CallDelayedMethod()
{
var delayer = new Delayer();
delayer.Execute(ThrowException);
}
public Task ThrowException()
{
throw new Exception();
}
I would still return a Task and leave it to the caller to ignore it by not awaiting it (fire and forget) or not.
Original answer
You are not following the best practices by using an async void signature in the class Delayer.
public async void Execute(Action action)
should be
public async Task Execute(Action action)
so you can await the call to Execute. Otherwise it is just a fire and forget operation and that makes catching exceptions difficult. By making it awaitbale you can do:
try
{
await Delayer.Execute(...);
}
catch(Exception ex)
{
....
}
From the best practices:
Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started.
Also, you should have Execute accept a Task if you want to pass awaitable actions to it:
public async Task Execute(Func<Task> action)
{
await Task.Delay(10).ConfigureAwait(false);
await action.Invoke();
}
Given the following code:
public async Task Send() // part of Sender class
{
// sync code
}
// //
private async Task HandleMessage()
{
// await sender.Send(); // exits HandleMessage immediately
sender.Send().Wait(); // works as expected, waiting to complete
DoOtherStuff(); // doesn't get hit with await
return;
}
RunRecurringTask(async () => await HandleMessage(), result);
public void RunRecurringTask(Action action, RecurringTaskRunResult result)
{
action();
result.DoStuff();
}
I thought that await tells the thread to come back when the awaited thing is complete, but it looks like for some reason that's not happening: the remaining code is never hit and everything just... stops. What could be causing this?
This is a console application in an Azure WebJob, for what it's worth. When Wait is used, I get the expected results, however with await, the job just completes.
You should never do async void unless you are writing a event handler. A Action with the async modifier is a async void method. You need to make the argument a Func<Task> and then do await action() in your RunRecurringTask
private async Task HandleMessage()
{
await sender.Send();
DoOtherStuff();
return;
}
RunRecurringTask(async () => await HandleMessage(), result);
//You also could do
//RunRecurringTask(() => HandleMessage(), result);
public async Task RunRecurringTask(Func<Task> action, RecurringTaskRunResult result)
{
await action();
result.DoStuff();
}
If you had other methods that where not marked with async you will need to change all of them up the call stack till you get to the entry point from the SDK. The SDK understands how to handle functions with a async Task return type since the 0.4.0-beta version.
I need a function to be executed in the background after the request is completed.
My code is something like:
[HttpPost]
public HttpResponseMessage Post([FromBody] List<JObject> lstData)
{
return PostMethod(lstData);
}
HttpResponseMessage PostMethod(List<JObject> lstData)
{
//do somework
Method1();
return myResult;
}
void Method1()
{
Method2();
}
async void Method2()
{
//do some work
await Task.Delay(25);
Method2();
}
When this scenario run, Post doesn't return at all.
I was handling it by creating a task that executes Method2(), but I an trying to make use of the Asynchronous Programming
When using async-await it is really best to let the async-await syntax flow from top to bottom of your application. Also, do not ever return async void unless it is a front-end event handler, you want to at least return async Task. async void will cause you to have undesired side effects such as losing the current context, and current methods could end up deadlocked/blocked at those calls.
I suggest you re-write your method stack to async Task and async Task<.Type>, you will be in a much better place that way :).
Example:
[HttpPost]
public async Task<HttpResponseMessage> Post([FromBody] List<JObject> lstData)
{
return await PostMethod(lstData);
}
async Task<HttpResponseMessage> PostMethod(List<JObject> lstData)
{
//do somework
await Method1();
return myResult;
}
async Task Method1()
{
await Method2();
}
async Task Method2()
{
//do some work
await Task.Delay(25);
}
And follow this the whole method chain down.
Supposed you have 2 async method define as bellow:
public async Task<TResult> SomeMethod1()
{
throw new Exception();
}
public async Task<TResult> SomeMethod2()
{
await Task.Delay(50);
throw new Exception();
}
Now if you await on those 2 methods the behavior will be pretty much the same. But if you are getting the task the behavior is different.
If I want to cache the result of such a computation but only when the task run to completion.
I have to take care of the 2 situation:
First Situation:
public Task<TResult> CachingThis1(Func<Task<TResult>> doSomthing1)
{
try
{
var futur = doSomthing1()
futur.ContinueWith(
t =>
{
// ... Add To my cache
},
TaskContinuationOptions.NotOnFaulted);
}
catch ()
{
// ... Remove from the pending cache
throw;
}
}
Second Situation
public Task<TResult> CachingThis2(Func<Task<TResult>> doSomthing)
{
var futur = SomeMethod2();
futur.ContinueWith(
t =>
{
// ... Add To my cache
},
TaskContinuationOptions.NotOnFaulted);
futur.ContinueWith(
t =>
{
// ... Remove from the pending cache
},
TaskContinuationOptions.OnlyOnFaulted);
}
Now I pass to my caching system the method that will execute the computation to cache.
cachingSystem.CachingThis1(SomeMethod1);
cachingSystem.CachingThis2(SomeMethod2);
Clearly I need to duplicate code in the "ConinueWith on faulted" and the catch block.
Do you know if there is a way to make the exception behave the same whether it is before or after an await?
There's no difference in the exception handling required for both SomeMethod1 and SomeMethod2. They run exactly the same way and the exception would be stored in the returned task.
This can easily be seen in this example;
static void Main(string[] args)
{
try
{
var task = SomeMethod1();
}
catch
{
// Unreachable code
}
}
public static async Task SomeMethod1()
{
throw new Exception();
}
No exception would be handled in this case since the returned task is not awaited.
There is however a distinction between a simple Task-returning method and an async method:
public static Task TaskReturning()
{
throw new Exception();
return Task.Delay(1000);
}
public static async Task Async()
{
throw new Exception();
await Task.Delay(1000);
}
You can avoid code duplication by simply having an async wrapper method that both invokes the method and awaits the returned task inside a single try-catch block:
public static async Task HandleAsync()
{
try
{
await TaskReturning();
// Add to cache.
}
catch
{
// handle exception from both the synchronous and asynchronous parts.
}
}
In addition to what I3arnon said in his answer, in case you ContinueWith on async method without the TaskContinuationOptions you specify, exception captured by the Task parameter you receive in the continuation handler can be handled in the following way:
SomeMethod1().ContinueWith(ProcessResult);
SomeMethod2().ContinueWith(ProcessResult);
With ProcessResult handler which looks like:
private void ProcessResult<TResult>(Task<TResult> task)
{
if (task.IsFaulted)
{
//remove from cahe
}
else if (task.IsCompleted)
{
//add to cache
}
}