0

I want my UI to display a loading overlay once i click on my button.

Option 1

MyCommand = new DelegateCommand(() => Task.Run(MyLongRunningTask);

private async Task MyLongRunningTask() {
    IsLoading = true;
    Thread.Sleep(5000);
    IsLoading = false;
}

Option 2

MyCommand = new DelegateCommand(MyLongRunningMethod);

private async void MyLongRunningMethod() {
    IsLoading = true;
    Thread.Sleep(5000);
    IsLoading = false;
}

Option 1 works, option 2 does not. I'm fine with that being the case, although i don't understand why. But i would really like to avoid having to wrap every execute into () => Task.Run(...).

Since async void seems to be a bad idea, i would like to keep using tasks. But i want to shorten the syntax, so im trying like mad but i cannot figure out how to put the () => Task.Run(..) in a method or class that derives from DelegateCommand.

Why doesnt this compile?

    private DelegateCommand GetAsyncCommand(Task t)
    {
        return new DelegateCommand(() => Task.Run(t));
    }

Anything similiar to this leads to either compile time errors or i me having to call

MyCommand = GetAsyncCommand(MyLongRunningTask());

Because of the () braces it instantly executes my task on instantiation.

This seems really convoluted to me. If i have to wrap all my long running tasks into this construct, why cant i build a smarter command that just does that for me given a task?

Edit: I just notices that Option 1 might not lock the UI, but it swallows exceptions which is horrible.

How do i set up a command that does not block the UI and throws any exceptions normally?

H H
  • 263,252
  • 30
  • 330
  • 514
  • Let me quote from Stephen Cleary's excelant article (https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming): 'Async void methods can wreak havoc if the caller isn’t expecting them to be async.' – ChristianMurschall Apr 04 '20 at 07:55
  • He also wrote an AsyncCommand. This will give you a clean syntax. https://github.com/StephenCleary/Mvvm.Async/blob/master/src/Nito.Mvvm.Async/AsyncCommand.cs – ChristianMurschall Apr 04 '20 at 07:57
  • [Avoid async void](https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming#avoid-async-void). It is intended for event handlers only. – Theodor Zoulias Apr 04 '20 at 09:52
  • The method `MyLongRunningTask` should give you a warning, that you shouldn't ignore: *This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.* – Theodor Zoulias Apr 04 '20 at 09:59
  • _"Why doesnt this compile?"_ because `Task.Run()` wants a `Func` or `Action` but you are giving it a `Task`. The compiler should tell you somethind similar. – Ackdari Apr 04 '20 at 11:23

1 Answers1

2

Thread.Sleep() is locking the current thread regardless where it is: in sync or async method. Async method has another way to pause the execution - await Task.Delay().

async is not responsible on Threading, async implements a state mashine that executes your code asynchroniously splitting it by awaitable parts. Task is responsible on Threading.

replace

Thread.Sleep(5000);

with

await Task.Delay(5000);

Note that async void is bad practice. Look.

aepot
  • 4,558
  • 2
  • 12
  • 24
  • But this would not help in releaving the UI thread from doing CPU heavy work. I have the impression that the `Thread.Sleep(...)` is simulation for a computation that does CPU heavy work. If that is the case it would be wise to use `Task.Run()` to run that code on a thread-pool thread. – Ackdari Apr 06 '20 at 09:24
  • 1
    @Ackdari agree but are you sure that heavy work in `async Task` is executing definetly in current Thread? I'm not. And I'm not sure that if I run 2 extra heavy async methods it will cause thread lock in 2nd method before completion of the 1st method. Thus `Sleep()` os not exact "heavy task" emulation but pure locking. – aepot Apr 06 '20 at 11:33