I am studying C# Asnc-await pattern and currently reading Concurrency in C# Cookbook from S. Cleary
He discusses wrapping old non TAP async patterns with TaskCompletionSource (TCS) into TAP constructs. What I dont get is, why he just returns the Task property of the TCS object instead of awaiting it TCS.Task ?
Here is the example code:
Old method to wrap is DownloadString(...):
public interface IMyAsyncHttpService
{
void DownloadString(Uri address, Action<string, Exception> callback);
}
Wrapping it into TAP construct:
public static Task<string> DownloadStringAsync(
this IMyAsyncHttpService httpService, Uri address)
{
var tcs = new TaskCompletionSource<string>();
httpService.DownloadString(address, (result, exception) =>
{
if (exception != null)
tcs.TrySetException(exception);
else
tcs.TrySetResult(result);
});
return tcs.Task;
}
Now why not just do it that way:
public static async Task<string> DownloadStringAsync(
this IMyAsyncHttpService httpService, Uri address)
{
var tcs = new TaskCompletionSource<string>();
httpService.DownloadString(address, (result, exception) =>
{
if (exception != null)
tcs.TrySetException(exception);
else
tcs.TrySetResult(result);
});
return await tcs.Task;
}
Is there a functional difference between the two? Is the second one not more natural?
By marking it async, the compiler will generate warnings, that it should be considered to await this method
You don't have to mark your own method as async
in order to get the "Task not awaited" warning. The following code generates the same warning for the calls to both T
and U
:
static async Task Main(string[] args)
{
Console.WriteLine("Done");
T();
U();
Console.WriteLine("Hello");
}
public static Task T()
{
return Task.CompletedTask;
}
public static async Task U()
{
await Task.Yield();
return;
}
Whenever you find yourself with a method only containing a single await
and that being the last thing it does (except possibly returning the awaited value), you should ask yourself what value it's adding. Aside from some differences in exception handing, it's just adding an extra Task
into the mix.
await
is generally a way of indicating "I've got no useful work to do right now, but will have when this other Task
is finished" which of course isn't true (you've got no other work to do later). So skip the await
and just return what you would have awaited instead.