My UI is asynchronous (Part 2)

We used the Dispatcher class in the last post to allow asynchrony. In this post we’ll use the power of TPL (Task Parallel Library) to update the UI.

We’ve seen TPL a bit with the Task.Factory.StartNew() method last time :

private void Asynchony1()
        {
            Task.Factory.StartNew(() =>
            {
                var message = this.LongRunningTask();
                Application.Current.Dispatcher.Invoke(new Action(() =>
                {
                    this.lblResult.Text = message;
                }));
            });
        }

But we’re mixing the long running method processing and the UI update in the same part of our code (the anonymous method). TPL allow us to define Task continuations. It means that we can add new code that will be executed only when the preceding thread is done or is in a certain state.

This time again we have to mention that our task has to be executed in the UI Thread. We do so by defining a parameter on the ContinueWith method call

    private void Asynchony1()
        {
            Task.Factory.StartNew<string>(() =>
            {
                return LongRunningTask();

            }).ContinueWith((re) =>
                            {
                                this.lblResult.Text = re.Result;
                            },
            TaskScheduler.FromCurrentSynchronizationContext());
        }

I slightly modify the StartNew call in order to indicate that it will return a result of type string. We use this return variable in the ContinueWith method via the lambda parameter. Calling the FromCurrentSynchronizationContext() method of TaskScheduler allow the code to be executed in the UI thread.

A bit further

This time we want to manage the case that an exception is being thrown in our long running method. We can do this by assigning two ContinueWith call to our StartNew method. With TaskContinuationOptions we can set the task to be executed only when the preceding Task was faulted or was ran to completion (and there are a lot more options available).

I used named parameters in my example, I think it’s better to see quickly which parameter correspond to which expected one.

private void Asynchony1()
        {
            var tPrincipal = Task.Factory.StartNew<string>(() =>
              {
                  //throw new Exception();
                  return LongRunningTask();

              });

            tPrincipal.ContinueWith(
                scheduler: TaskScheduler.FromCurrentSynchronizationContext(),
                continuationAction: (re) =>
                {
                    this.lblResult.Text = re.Result;
                });
            tPrincipal.ContinueWith(
                continuationOptions: TaskContinuationOptions.OnlyOnFaulted,
                scheduler: TaskScheduler.FromCurrentSynchronizationContext(),
                cancellationToken: CancellationToken.None,
                continuationAction: (re) =>
                {
                    this.lblResult.Text = "Error has occured";
                });
        }

It looks a bit verbose but it’s very powerful I think. To test it just uncomment the throw new exception code and it will execute the second ContinueWith call.