0

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?

Sebi
  • 4,262
  • 13
  • 60
  • 116
  • What is the `CommandHandler`? – Pavel Anikhouski Oct 03 '20 at 15:21
  • A class that routes clicks (or supposedly any form of user interaction). It's been taken (copied) from https://stackoverflow.com/questions/12422945/how-to-bind-wpf-button-to-a-command-in-viewmodelbase – Sebi Oct 03 '20 at 15:23
  • 1
    It seems that `CommandHandler` just implements `ICommand` interface like relay command. And it executes the passed `Action` syncronously. To have an asynchronous execution you might use [async relay command](https://stackoverflow.com/questions/20170923/relay-command-can-execute-and-a-task) or set `IsAsync` in xaml – Pavel Anikhouski Oct 03 '20 at 15:32
  • Thanks! I'll give it a try. Do you know a way around Task.Delay(100);? – Sebi Oct 03 '20 at 15:35
  • I see no reason to use `await Task.Delay(100)` in code. If you need some delay, there is [`Delay`](https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.bindingbase.delay?view=netcore-3.1) property in binding – Pavel Anikhouski Oct 03 '20 at 15:38
  • I don't follow why that delay is there. I also don't see how using a delay in a binding might be of any use here. Or where chaining of any async calls comes in. – Andy Oct 03 '20 at 15:48
  • If I remove it VS complains that it's synchronous code; the file read and rendering are done synchronously, but I don't want to lock the UI while this happens; I want to leave the updating part to the bindings. – Sebi Oct 03 '20 at 15:56

1 Answers1

2

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.

Andy
  • 11,864
  • 2
  • 17
  • 20