Ever write a function that calls another function that happens to be asynchronous? Intellisense complains that the function being called is not being awaited. What did you do? Add the keyword await and voila! Your issue was resolved.
But what is your code doing?
No time to ponder on that, create the pull request and wait for your lead to dig into you and hope that they don't ask you to explain the use of await
or why you had to mark your function as async
. You can't give them the inclination that you don't know what you're doing!
What it's not
Async programming is not parallel programming. It does not mean "thread A go do X" and "thread B go do Z". It means the "thread" that you are using right now, will do all the tasks but in a order that will best optimize performance. A popular example is someone making breakfast. Say you are making eggs and toast. Your first step is to heat up the pan. While the pan is heating up, take the eggs out of the fridge and the bread out of the pantry. Now your pan is hot, break the eggs to start frying. While the eggs are frying, place the bread in the toaster. Your eggs are done. Plate them. Your toast is ready. Plate that too. Done. It took one person, one thread but the tasks were done in a manner where both items would be cooked simultaneously.
Async
Async
enables the use of the await
keyword in the method. A method marked with an async keyword, executes like any other function until it gets to the line with the await
keyword.
Await
Await
provides a non-blocking way to start a task. How? Through method control flow. When a task to download a file has been called, the thread does not wait until its completion. It yields control to the calling method to continue work until the result of the file download is needed. The await
keyword indicates the point at which the method is suspended and the control has been passed to the calling function if the task has not been completed.
If the awaited async
method has been completed, it returns the result stored into a Task
object.
Here is a helpful diagram of the control flow:
Task Asynchronous Programming (TAP) model
C# uses the Task Asynchronous Programming (TAP) model to provide an abstraction over asynchronous code. This allows us to read and write code in a synchronous fashion while performing asynchronous tasks. The compiler does all the work figuring out when methods need to be suspended and the order of their execution flows. Doing this work without language support would require a lot of code written by the developer that would convolute the intent of the business logic being executed.
The TAP model essentially utilizes Task
objects to hold the async method's results, progress, and other information. You can store the result of an async method to an object of type Task
. When the result is needed, you can await
that Task
object to retrieve the result of the async method.
public async Task cookBreakfast() {
// Start toasting the bread and let me hold onto an object
// that I can use to retrieve the result later
Task toastTask = toastBreadAsync();
Console.log("Frying eggs");
// I need the result of ToastBreadAsync() now
await toastTask;
Console.WriteLine("toast is ready");
}
public async Task toastBreadAsync(){
Console.WriteLine("Starting on toast");
await someToastyThings();
Console.WriteLine("Done Toasting!");
}
The result of the above code execution would look something like this:
"Starting on toast"
"Frying eggs"
"Done Toasting!"
"toast is ready"
Lets walk through this.
- cookBreakfast() is called.
- toastBreadAsync() is called
- "Starting on toast" is printed
- someToastyThings() is called and awaited. Yields control to cookBreakfast()
- toastBreadAsync() has it's Task stored into toastTask
- "Frying eggs" is printed
- toastTask is awaited. Control is back to someToastyThings(); Is it done? Lets say it is.
- "Done Toasting" is printed
- Control comes back to cookBreakfast
- "toast is ready" is printed
Sync vs Async
It is important to call out the differences in building asynchronous applications vs synchronous ones specially with the TAP model.
- You'll note that a synchronous method returns a value when the method is complete. However, an asynchronous method will return a
Task
immediately and when it eventually completes, it will store the result in theTask
. - The next is control flow. When a method is awaited and the results of the method are not yet available, the control is yielded to the calling function. On the flip side, with synchronous programming, control will continue to the next line.
Further Reading
I wasn't born with this information. Here are some really good resources to go deeper on the subject.
I hope this helped scratch the surface on asynchronous programming in dotnet. There is a lot more fancy things that can be done when it comes to this topic but having the fundamental understanding of what is happening in your application is the first step.
Go onward with your PRs with confidence!
Top comments (8)
Great post! Keep them coming! ๐
Well done.
This is awesome!
Great post! I have used async/await in JavaScript but I'm not sure whether I really knew what was going on until I read this. Thank you!
'I wasn't born with this information.' haahaha that was good acknowledgement. i often say that to people too
making the breakfast: best example ever!
WoW!!
nice article.