Scheduling async tasks

It's very common for a XAML application (or really, any kind of application) to have to load a good amount of data on startup to populate its initial state. When doing so it is often important to avoid blocking the UI, for example while reading the filesystem or fetching data from an external API, as we have no idea how long those operations will take. For this reason we generally want data to be fetched from a worker thread, in an asynchronous fashion, while keeping the UI reactive to user actions.

It's of course not limited to the initial state, we want to be able to do asynchronous work almost every time the user clicks on a button or edit a text box.

In the .net world async work can be easily managed by using System.Threading.Tasks.Task, a class representing asynchronous operations and for which C# has nice syntaxic sugar in the form of the (now quite common) async/await keywords.

The general pattern to do async in a XAML context is the following, here in the context of an event handler for a Click event:

// Page method to handle UI event, bound to the UI thread.
private void Button_Click(/*...*/) {
    // Schedule a task on a worker thread. Does not block.
    Task.Run(async () => {
        var res = await ViewModel.DoSomethingExpensive();

        // This is called only once the expensive work is completed.
        Debug.WriteLine("expensive task done");
    })

    // This will be executed directly after the call to Task.Run,
    // and before the scheduled task started.
    Debug.WriteLine("button clicked");
}

Async event handlers

Public methods and event handlers of a Windows.UI.Xaml.Controls.Page (a XAML page) are run from the UI thread, which means that any time-consuming operation will make the UI hang. When using Task.Run we provide a lambda function, wrapping the set of work we would like to run from a worker thread. The lambda is expected to return a Task, a promise-like construct that can be chained, awaited, or cancelled. async is some syntaxic sugar to say that we will return something "awaitable" and will manipulate Task objects. In an async context, such as our lambda, we can use await to block the execution of the current scope until a Task is considered done.

The equivalent without async/await looks like this, the only change here is the use of Task.Result:

// Page method to handle UI event, bound to the UI thread.
private void Button_Click(/*...*/) {
    // Schedule a task on a worker thread. Does not block.
    Task.Run(() => {
        var res = ViewModel.DoSomethingExpensive().Result;

        // This is called only once the expensive work is completed.
        Debug.WriteLine("expensive task done");
    })

    // This will be executed directly after the call to Task.Run,
    // and before the scheduled task started.
    Debug.WriteLine("button clicked");
}

In the case of a click handler the method can be simplified by tagging it as async and getting rid of Task.Run:

private async void Button_Click(/* ... */) {
    var task = ViewModel.DoSomethingExpensive();
    // This will be executed right away, we didn't start the task yet.
    Debug.WriteLine("button clicked");

    // Start the task by "awaiting" it.
    var res = await task; 

    // This is called only once the expensive work is completed.
    Debug.WriteLine("expensive task done");
}

We could of course await Viewmodel.DoSomethingExpensive() directly, but that would mean our log "button clicked" wouldn't show up until the task is completed.

Update UI from worker threads

We've seen how easy it is to schedule work on a worker thread. But here is the gotcha: objects created from the UI thread must only be updated from the UI thread. Any attempt to modify a XAML element, or an ObservableCollection, or even a property from a class implementing INotifyPropertyChanged will result in an exception:

System.Exception{
    "The application called an interface that was marshalled for a different thread.
    (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))"
}   

So, how do we update the UI from our worker thread without triggering this exception? For this we need to use Window.Current.Dispatcher:

private async void Button_Click()
{
    // Do something fancy from our worker thread.
    await ViewModel.DoSomething();

    // Then schedule a UI update.
    await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        // Anything done in context is run from the UI thread.
        //
        // For example we are safe to interact with a XAML element.
        myElement.Text = "Clicked!";
    });
}

Note that the lambda function passed to Dispatcher.RunAsync should not be defined as async.

That's great, we can now update our UI safely. But it's also overly verbose considering that we have to do this every time we want to schedule UI updates. Instead we can simplify our life with a short helper function, uiDispatch:

// A XAML page. MainPage methods are bound to the UI thread.
public sealed partial class MainPage: Page {
    // Define a helper function to update our UI
    private readonly Func<DispatchedHandler, IAsyncAction> uiDispatch;

    // Page constuctor.
    public MainPage() {
        InitializeComponents();

        // Instantiate the helper function
        var dispatcher = Window.Current.Dispatcher;
        uiDispatch = (DispatchedHandler callback) =>
            dispatcher.RunAsync(CoreDispatcherPriority.Normal, callback);

        /* ... */
    }

    // Event handler.
    private async void Button_Click()
    {
        // Do something fancy from our worker thread.
        await ViewModel.DoSomething();

        // Then schedule a UI update using our helper.
        await uiDispatch(() =>
        {
            // Anything done in context is run from the UI thread.
            //
            // For example we are safe to interact with a XAML element.
            myElement.Text = "Clicked!";
        });
    }
}

Now we can easily read and follow what is happening in our event handler with only minimal boilerplate. Also, the same pattern can be used from a ViewModel.

Async work from a Page constructor

Let's say we have a MainPage (a XAML page), and a MyViewModel (our view model associated with MainPage). MainPage displays some content we would like to populate at startup, for example a list of songs from a music directory, or weather data from a weather API. It's a natural fit to move that logic to our view model object, the result can then be added to our ObservableCollection.

We cannot define constructors as async, that wouldn't be valid C#, so we need to instead explicitely schedule our work to a worker thread.

A common pattern is the following.

Our view model takes the application dispatcher as input and defines async methods to manipulate data:

public class MyViewModel {
    // An observable collection on which XAML element can be bound.
    public ObservableCollection<MyModel> data = new ObservableCollection<MyModel>();

    // A helper function to easily run UI updates from the UI thread
    private Func<DispatchedHandler, IAsyncAction> uiDispatch;

    public MyViewmodel(CoreDispatcher uiDispatcher) {
        // Define our helper function
        uiDispatch = (DispatchedHandler callback) =>
            uiDispatcher.RunAsync(CoreDispatcherPriority.Normal, callback);
    }

    public async LoadData() {
        // Get our data, potentially a time-consuming operation.
        var items = await FetchDataFromSomewhere();

        // Schedule a UI update on the UI thread
        await uiDispatch(() => {
            foreach (var item in items) {
                // Update our ObservableCollection
                data.Add(item);
            }
        });
    }

    private async Task</*...*/> FetchDataFromSomewhere() {
        /* ... */
    }
}

And from our page constructor we first create the view model object then directly schedule the data fetching operation:

// A XAML page. MainPage methods are bound to the UI thread.
public sealed partial class MainPage: Page {
    // Page constuctor.
    public MainPage() {
        InitializeComponents();

        var dispatcher = Window.Current.Dispatcher;
        /* ... */

        // Instantiate our view model
        ViewModel = new MyViewModel(dispatcher);
        // Load data from a worker thread
        Task.Run(async () => await ViewModel.LoadData());
    }
}