I recently observed a code review between two developers.
The following code was submitted:
public async Task<List<Thing>> GetThings()
{
try
{
var endpoint = $"{Settings.ThingEndpoint}/things";
var response = await HttpClient.GetAsync(endpoint);
return JsonConvert.DeserializeObject<List<Thing>>(await response.Content.ReadAsStringAsync());
}
catch (Exception e)
{
Log.Logger.Error(e.ToString());
return await Task.FromException<List<Thing>>(e);
}
}
Which received the following review comments:
There is absolutely no need to return await Task.FromException>(e), this is something you do when dealing with non awaited task. In this case the catch will capture whatever exception var response = await HttpClient.GetAsync(endpoint); will throw. You should just remove it and catch the exception as is
I do not fully understand why not to use Task.FromException in this case, so i have the following Questions:
Whatever you return from async methods are wrapped in a Task . If you return no value(void) it will be wrapped in Task , If you return int it will be wrapped in Task<int> and so on.
Explicitly sets the return value for a task. In a DAG of tasks, a task can call this function to set a return value. Another task that identifies this task as the predecessor task (using the AFTER keyword in the task definition) can retrieve the return value set by the predecessor task.
The Task<TResult> return type is used for an async method that contains a return statement in which the operand is TResult . In the following example, the GetLeisureHoursAsync method contains a return statement that returns an integer. The method declaration must specify a return type of Task<int> .
`Task. FromResult` returns an already-completed task. It's useful if you have a task-returning method (i.e., from an interface) and you want to return an already-completed task.
The reviewer is entirely correct.
The only situation you would use Task.FromException
is when you're in a method you cannot or won't implement using async
and await
, and you want the result of the task should be an exception.
Idiot example but anyway:
public Task<int> NotReallyAsync()
{
if (new Random().Next(2) == 0)
return Task.FromResult(42);
return Task.FromException<int>(new InvalidOperationException());
}
So let's deal with your questions one by one:
The reviewer is saying that Task.FromException
should only be used in a non-async
/await
method, in an async
/await
method, you should instead just rethrow the exception:
catch (Exception e)
{
Log.Logger.Error(e.ToString());
throw;
}
or if you implement an exception filter:
catch (Exception e) when (Log.Logger.ExceptionFilter(e)) { }
Yes, the reviewer is correct.
async
/await
is to be able to write your method in a normal manner, so write a normal throw statement or a normal catch-block.async
/await
methods, and only that.Task.FromException is generally used when you want to rely on status of Task if a known failure condition is met. For example, if an object is null you know you should return a faulted task.Client can use state of task as faulted to show appropriate message to user.I modified the code just to tell you on example.
public async Task<List<Thing>> GetThings()
{
try
{
var endpoint = $"{Settings.ThingEndpoint}/things";
var response = await HttpClient.GetAsync(endpoint);
var obj = JsonConvert.DeserializeObject<List<Thing>>(await response.Content.ReadAsStringAsync());
if(obj==null)
{
return await Task.FromException<List<Thing>>(new NullRefernceException());
}
else
{
}
}
catch (Exception e)
{
Log.Logger.Error(e.ToString());
throw;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With