2

(C# code below)

I am just staring to learn about Async and Await. I've checked a few articles and tutorials, and I thought I got the concept behind it but I seem to hit the wall when implementing some practical examples.

This is the New() of my UI class:

Public Sub New()

    ' This call is required by the designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.
    PopulateUI()

End Sub

...so the key here is PopulateUI(). I want to use this function without blocking the UI.

The only option I came out with which the compiler could accept is:

Private Async Sub PopulateUI()
    Await Task.Run(Sub()
            ' Do some stuff that populates the UI comboboxes
        End Sub)
End Sub

...this option does not work, and I think it is because Task.Run runs in a different thread so weird things occur when updating the comboboxes (sorry for being so vague about the description, I really don't know better).

So I found a similar SO question which didn't have any satisfactory answer, which makes me think is not that simple. Hopefully I'm wrong.


C# version:

public MyUI()
{
    // This call is required by the designer.
    InitializeComponent();

    // Add any initialization after the InitializeComponent() call.
    PopulateUI();
}

Private async void PopulateUI()
{
    await Task.Run(() => 
    {
        // Do some stuff that populates the UI comboboxes
    })
}
5
  • 2
    What exactly are you doing in PopulateUI? Populating UI with content should be done on the UI thread; you should only run stuff that fetches/initializes the data off that thread. Commented Aug 16, 2015 at 12:40
  • 2
    Also, you should avoid doing too much in the constructor. You should consider using one of your component’s lifetime events (e.g. Loaded if this is WPF). Commented Aug 16, 2015 at 12:44
  • I have edited your title. Please see, "Should questions include “tags” in their titles?", where the consensus is "no, they should not". Commented Aug 16, 2015 at 12:46
  • @poke PopulateUI fetches the data from the database (a single call to the db manager, not awaitable) and then populates the UI. The db call should be awaitable, but it would just be changes would be quite costly for me at this point in time. Plus I am starting to doubt if that would really be the end of my problems. So would it be a terrible idea to perform a Task.Run() of the fetch operation, wait for it in the main thread and then populate the combobox in the main thread as you suggest? Commented Aug 16, 2015 at 12:47
  • @poke About the event, it is a good idea that I already tried. But instead of WPF I am using a "non-WPF user control" which leads to difficulties catching the "finished loading" event: stackoverflow.com/questions/1782708/… Commented Aug 16, 2015 at 12:55

3 Answers 3

4

The db call should be awaitable, but it would just be changes would be quite costly for me at this point in time... So would it be a terrible idea to perform a Task.Run() of the fetch operation

As you noted, the ideal solution is to make it asynchronous all the way. So the solution below is a hack (to avoid too many code changes) and not a best practice.

That said, you can use async void and Task.Run here; you just have to be careful with your exception handling:

private async void PopulateUI()
{
  ... // Load initial view - "Loading..." message or whatever.
  try
  {
    var data = await Task.Run(() => 
    {
      ... // Read data from database.
    });
    ... // Update UI with data.
  }
  catch (Exception ex) // Not a typo. Catch all exceptions.
  {
    ... // Handle error - display message to user or whatever.
  }
}
Sign up to request clarification or add additional context in comments.

Comments

2

First of all: be very careful with async void ...: you will not see any exceptions from such a call.

I don't know if this is the canonical pattern (if there is such a thing), but what I usually do is to create a method void CheckResult(Task task), which uses ContinueWith to append the necessary error handling (usually checking if the Task has faulted and logging/displaying an error dialog.

Then I would do something like:

public MyUI()
{
    // This call is required by the designer.
    InitializeComponent();

    // Add any initialization after the InitializeComponent() call.
    CheckResult(PopulateUI());
}

private async Task PopulateUI()
{
    var data = await FetchUiData();
    SetControlValues(data); // or whatever
}

private static void CheckResult(Task t) {
    // We want to be doing this on the UI thread
    var sched = TaskScheduler.FromCurrentSynchronizationContext();
    t.ContinueWith(() => {
       // check if "t" faulted and perform appropriate action
    }, sched);
}

Comments

0

Use Dispatcher when accessing the UI controls specifically while setting the properties of UI Controls, the weird things might go away. Fetch data from wherever in Task.Run but the code which is setting properties in UI Controls should be accessed using dispatcher. Probably this will be your issue.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.