Sebi
Sebi

Reputation: 4522

Is this the right way of chaining async calls (C#/WPF)?

Going through:

https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

and

How to bind WPF button to a command in ViewModelBase?

I've come up with the following code of reading and rendering file contents to a window asynchronously (or synchronously?):

class AppliedJobsViewModel
{
    private TexParser texParser; 

    private ICommand _openTexClick;
    public ICommand OpenTexClick
    {
        get
        {
            return _openTexClick ?? (_openTexClick = new CommandHandler(() => ReadAndParseTexFile(), () => CanExecute));
        }
    }
    public bool CanExecute
    {
        get
        {
            // check if executing is allowed, i.e., validate, check if a process is running, etc. 
            return true;
        }
    }

    public async Task ReadAndParseTexFile()
    {
        if (texParser == null)
        {
            texParser = new TexParser();
        }
        // Read file asynchronously here
        await ReadAndParseTexFileAsync();            
        string[][] appliedJobs = texParser.getCleanTable();
    }

    private async Task ReadAndParseTexFileAsync()
    {
        texParser.ReadTexFile();
        await Task.Delay(100);
    }

    public ObservableCollection<AppliedJob> AppliedJobs {
        get;
        set;
    }
}

which "works" but I don't like that Task.Delay(100) (constant wait time).

VS also tells me at line:

    return _openTexClick ?? (_openTexClick = new CommandHandler(() => ReadAndParseTexFile(), () => CanExecute));

"Because this call is not awaited, the execution of this method continues before the call is completed".

But this method is what binds the code to the wpf view. Isn't it async by default?

Upvotes: 0

Views: 166

Answers (1)

Andy
Andy

Reputation: 12276

Just dropping an async in a method or task signature doesn't mean you're doing anything asynchronously.

Read this:

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/using-async-for-file-access

Note that it is not just a case of adding async to the name than makes File.ReadAllTextAsync asynchronous.

You should read up on async and await. eg https://blog.stephencleary.com/2012/02/async-and-await.html

This is a complex subject. It will take careful reading and consideration to understand.

Once you've read that, reconsider your code.

In that warning your ide was telling you your code is synchronous.

You have synchronous code in there reading that file.

Sticking an await some-other-code in there does not make your file reading code asynchronous, it just means the ide sees an await Task. A pointless await task.

Now you know it's pointless.

Take out

await Task.Delay(100);

Replacing your file reading code with

 string contents = await File.ReadAllTextAsync(filePath); 

Could make the file reading asynchronous.

If this is a large file being read into memory then just reading it might have significant overhead and your UI may well remain responsive.

If you were doing some substantial processing of a file then you would probably do better pushing that onto a background thread.

You can do

 await Task.Run(() => ProcessFileText(someurl));

Here ProcessFileText would be a Task or maybe Task<string[]> and return a string or string array. It would run on a background thread and return once completed. Seeing as this is a different thread then it won't freeze your UI if it takes a while to do it's work.

Note that since it is now not on the UI thread this task cannot read or write UI properties or you'll get threading errors. Code in the line(s) after that await is back on the ui thread.

Upvotes: 2

Related Questions