Async, Await and the UI Problem
Written by Mike James   
Thursday, 25 February 2021
Article Index
Async, Await and the UI Problem
The flow of control
Advanced await



DoEvents and dangers

If you have used the DoEvents command in Visual Basic you might see that this is a very similar mechanism in that it allows the UI thread to process the Dispatch queue but also notice that this is more sophisticated in that another thread is used to do the work.

Just as there were dangers in using DoEvents and similar constructs there are even more dangers in using async/await. For example, as the UI stays responsive, the user can click the same button more than once and create multiple tasks. Usually this is not a good idea and so the event handler usually disables the button while the task is running:

private async void button1_Click(
object sender, RoutedEventArgs e)
button1.IsEnabled = false;
await DoWork();
button1.IsEnabled = true;

If you try writing this simple idea using threads and callbacks you will see just how neat the async/await pattern is.

Multiple awaits

This example just gets us started because it raises a few questions of its own. In particular, we still have the problem of allowing the worker code to update the UI. The key factor is that the event handler can update the UI each time it is restarted so we could use an approach where the work is broken into chunks which return to the event handler for an update.

For example:

private async void button1_Click(
object sender, RoutedEventArgs e)
button1.IsEnabled = false;
await DoWork();
textBlock1.Text = "First work done";
await DoWork();
textBlock2.Text = "second work done";
button1.IsEnabled = true;

In this case what happens is that DoWork runs for 5 seconds and then summons the UI thread back to continue the event handler from where it left off. This results in the text "First work done" being displayed. Then the second await lets the UI thread get on with processing the Dispatcher queue, only to be called back to continue from where it left off 5 seconds later. Hence we see the second work done update message.

OK, so you could break work down into chunks like this and even put the chunks into a for loop, say, to automate the process:

private async void button1_Click(
object sender, RoutedEventArgs e)
button1.IsEnabled = false;
for (int i = 0; i < 5; i++)
await DoWork();
textBlock1.Text = "Chunk "+i.ToString();
button1.IsEnabled = true;

However if all you want to do is report the progress of a longer computation via the UI then there is an alternative way of doing the job by passing in a method that can be called on the UI thread by the worker.

Getting results

An even more important issue, however, is how the worker thread can return a result. For simplicity all of the examples so far have returned void, but in real life the worker usually returns some value, usually a complex object.  Getting result back turns out to be remarkably easy.

To return a type t say we simply change the Task to Task<t> and return a t at the end of the code run on the worker thread.

For example to return an int from DoWork you would write:

Task<int> DoWork()
return TaskEx.Run(() =>
for (int i = 0; i < 10; i++)
return 500;

where the code run on the new thread returns 500 just to show that it works.

In the asychronous method we simply call the new DoWork in the obvious way:

int result=await DoWork();
textBlock1.Text = result.ToString();

Notice that there are a few points of potential confusion.

The return type of DoWork is stated as Task<int> yet it actually seems to return an <int> to the calling routine. Also there now appear to be two returns in DoWork, the one that returns the Task<int> and the one that returns the int. If you have followed how all of this works then this should seem understandable if potentially confusing.

This article has explained how the new async/await facilities can make writing good UI code easy but the same ideas apply more generally and you can write asynchronous methods that can be called from any thread, not just the UI thread. There are also lots of other facilities - a cancellation mechanism, a progress reporting mechanism and so on - that make the whole even more flexible.


Deep C#

 Buy Now From Amazon


 Chapter List

  1. Why C#?

    I Strong Typing & Type Safety
  2. Strong Typing
    Why Strong Typing
  3. Value & Reference
  4.    Extract Value And Reference
  5. Structs & Classes
    Structs & Classes 
  6. Inheritance
  7. Interfaces & Multiple Inheritance
    Extract Interface
  8. Controlling Inheritance
    II Casting & Generics
  9. Casting - The Escape From Strong Typing
    Extract Casting I ***NEW!
  10. Generics
  11. Advanced Generics
  12. Anonymous & Dynamic
    III Functions
  13. Delegates
  14. Multicast Delegates
  15. Anonymous Methods, Lambdas & Closures
    IV Async
  16. Threading, Tasks & Locking
  17. The Invoke Pattern
  18. Async Await
  19. The Parallel For
    V Data - LINQ, XML & Regular Expressions
  20. The LINQ Principle
  21. XML
  22. LINQ To XML
  23. Regular Expressions
    VI Unsafe & Interop
  24. Interop
  25. COM
  26. Custom Attributes
  27. Bit Manipulation
  28. Advanced Structs
  29. Pointers 

Extra Material






or email your comment to:

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.






Last Updated ( Thursday, 25 February 2021 )