C# Async/Await - The death of callback hell.
What is async/await?
Simply put, async and await are syntactic sugar for the handling callbacks. The basic idea is that when you call a method with await, it tells the compiler to wrap that bad boy in a Task and to pass it to the Task Scheduler. The Task Scheduler then executes the Task, and then the code following the await that is dependant on the Task is run. The two following examples are functionally equivalent, but the Async/Await way is more readable.
Callback flow
class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Run Callack");
GetPosts( results => Console.WriteLine($"GET /Posts (Callback):{results}") );
}
static void GetPosts( Action<string> callback )
{
using (var client = new WebClient())
{
client.DownloadStringCompleted += (sender, e) =>
{
Console.WriteLine( "Runing WebClient Callback");
string pageSourceCode = e.Result;
callback(pageSourceCode);
};
client.DownloadStringAsync(new Uri("https://jsonplaceholder.typicode.com/posts/"));
}
}
}
Async/Await flow
class Program
{
public static async Task Main(string[] args)
{
Console.WriteLine("Run Async");
Console.WriteLine($"GET /Posts (async): {await GetPostsAsync()}");
}
static Task<string> GetPostsAsync()
{
using (var client = new HttpClient())
{
return new HttpClient().GetStringAsync("https://jsonplaceholder.typicode.com/posts/");
}
}
}
*See The Awesomeness that is async Main()
WTF are Tasks
Task are basically what they sound like they are, little bits of work, that can be done asynchronously. They generally come in two varity: Task and Task<T> where T is the “return” value of the Task. In case it isn’t obvious, you would use the generic Task<T> when you need to retrieve a value, otherwise just return the plain ol Task.
Got’yas
There are a couple things you need to keep in mind when working with async/await:
Tasks are not threads.Tasks that areawaited run in a sequential queue unless told to do otherwise, ieParalell.ForEach(). Think nested callbacks for eachawait(not exactly but close enought for this article).Tasks may be run on a different thread then called, but the results are then marshalled to the calling thread.Tasks are cheap but not free. Every time you wrap/unwrap aTaskwithawaityou are paying a cost.
Da Rulze
- If you don’t need the
awaited value in a method, don’t use theasynckeyword and just return the unadulteratedTask. - Methods should return either
TaskorTask<T>except for when called by an event delegate, ie.Button.clickedthen they may returnvoid1. - Method names should end with the word ‘Async’.
- “Async all the way”, Don’t be switching between synchronous and asynchronous. Pick one, and be confident with your decision.
Until next time, keep Hackin’.