Functioning with Tasks, Async, Await and the Core Dispatcher
Hi All,
This Blog article is written for the explicitly purpose of getting functional with Tasks, Async, Await and the Dispatcher. It will discuss the basics as well as a few more advanced scenarios. It is not intended in anyway to provide in depth knowledge on any of these topics, it is purely a How-To in 10 minutes Guide written with minimal technical jargon. The article does however assume some programming knowledge with C# and Xaml.
What is Async
Async is essentially a method to keep your UI responsive while you have long running tasks that would otherwise clog it up. You mark methods with this keyword to indicate it contains awaitable functions or tasks.
What is Await
Await is simply a keyword that tells the compiler "I am going to run this function on a different thread, but keep doing your thing for the UI, and when I am done, I want to finish out the code below on the context that I was on when I went to go do this work".
What are Tasks
Tasks are essentially pieces of code that you delegate to run on a thread determined by the scheduler(essentially means you don't know which thread it will run on).
What is the Core Dispatcher
The Core Dispatcher is essentially a messenger between Tasks running on a thread and the UI thread, allowing you to return to the UI context from a different thread.
The Basics
So lets start off with a basic use case scenario for this. I want to bind a text block in my UI to something from a server response, or in a deeply nested 2 million line xml file that takes minutes to parse. What does this look like?
But wait, this is still binding my UI! What's the deal? Well in short without getting into the gritty details, you are returning type void, you need to return type Task<t> (where t does not necessarily need to be specified). It boils down to whether or not the compiler is creating a state machine for you or not. The problem here is that returning type Task results in a compilation error if you are responding to a button click (button event handlers must return void). So what do you do? The following is what I usually do to free up the UI.
When you click the button that is hooked up to DownloadSomething, it initiates the ButtonClick() function, but continues to run and then returns void immediately, no matter how long your ButtonClick() function takes. This allows your UI to continue being responsive. String s gets set to whatever is provided, and your TextBlockText is now equal to s and your UI is happy.
Making your own Tasks
Ok, well that is nice and all, but you want to do some long running task that is not built in that is awaitable. Enter Task.Run(() => {...}); So lets go ahead and show my implementation of provideRealText();
Awesome! But for some reason, you have to bind your s inside the task...Oh no! Not Marshaling errors! Without getting too complex, what is essentially happening is Task.Run(() =>{...}); schedules tasks on the thread pool, and when a thread becomes available, it runs on that thread in that context. Chances are that it is NOT the UI thread and therefor your NotifyPropertyChanged event is having difficulty finding its way back to the UI to tell it "Hey, I'm different, you best change to match".
Enter the Core Dispatcher
The core dispatcher is essentially a messenger that will take functions back to the UI context to be run there. So since setting the textblock fires the NotifyPropertyChanged event, which needs to find its way to the UI thread, you need to set the TextBlockText on the UI thread so the event can find its way easily. This looks like below.
But My Dispatcher, Window, Current Window etc is NULL?!?!?!?
Ah, I like how you separate out your code :). Unfortunately there is not a globally accessible Dispatcher like in Windows Phone, so to get around this issue, all you have to do is cache your dispatcher while on the UI thread. What I usually do to get around this is in whatever static class is holding my global app state data I have a static dispatcher and I pass a reference to the dispatcher on app startup.
Below is App State with the Dispatcher
Below is Caching the Dispatcher
Below is using the Dispatcher
So that concludes this blog post on getting functional with Tasks, Async and Await. Hopefully that removed all of the complexities and provided a straight forward How-To without concerning itself with anything else.
Comments
- Anonymous
December 15, 2013
Simple and clear explanation. Would be nice if some real time example used like showing preloader on UI while making a service call.