简体   繁体   English

等待/异步任务不等待

[英]await / async task not waiting

I am experiencing some confusion with Tasks and the async/await key words. 我对Tasks和async / await关键字感到有些困惑。 I understand that you should NOT mix async and blocking code. 我知道您不应该混合使用异步代码和阻塞代码。 Or at least what my interpretation of mixing them is: 或者至少我对混合它们的解释是:

Don't make calls to blocking API's from non- async methods. 不要从非异步方法中调用阻止API。 So here's my issue. 所以这是我的问题。

I am trying to await a method, then update the UI accordingly. 我正在尝试等待方法,然后相应地更新UI。 The issue is that the only way to await an async method() call is from within and async method(). 问题是等待异步方法()调用的唯一方法是从内部和异步方法()。

Here's an example: 这是一个例子:

private RelayCommand<Options> _executeCommand;

public RelayCommand<Options> ExecuteCommand
{
    get
    {                
        return _executeCommand ?? (_executeCommand = new RelayCommand<Options>(async (options) =>
        {
            Completed = false;                    

            var cancellationTokenSource = new CancellationTokenSource();

            await RunValidation(options, cancellationTokenSource.Token);

            Completed = true;

        }));
    }
}

This code runs the method properly and awaits. 此代码正确运行该方法并等待。 The issue is when I return. 问题是我什么时候回来。 For some reason when setting the Complete flag the buttons dependent on this flag are not toggled. 由于某些原因,设置完成标志时,依赖于该标志的按钮不会切换。 If I comment the await code, then the buttons are toggled correctly. 如果我注释了等待代码,则按钮将正确切换。 So assumed I was not returning on the UI thread, so I tried using this code instead: 因此,假设我没有返回UI线程,所以我尝试使用以下代码:

private RelayCommand<Options> _executeCommand;

public RelayCommand<Options> ExecuteCommand
{
    get
    {                
        return _executeCommand ?? (_executeCommand = new RelayCommand<Options>(async (options) =>
        {
            Completed = false;                    

            var cancellationTokenSource = new CancellationTokenSource();                                                                

            var context = TaskScheduler.FromCurrentSynchronizationContext();

            await RunValidation(options, cancellationTokenSource.Token).ContinueWith(t => Completed = true, context);

            //Completed = true;

        }));
    }
}

Here is the RunValidation() method: 这是RunValidation()方法:

private async Task RunValidation(Options options, CancellationToken token)
{            
    await _someService.SomAsyncMethod(options, token));
}

If you notice, the ExecuteCommand has an async key word before the (options) parameter that is passed to the command. 如果您注意到,ExecuteCommand在传递给命令的(options)参数之前有一个异步关键字。 If I remove the async key word then I have to modify the call to the RunValidation() method. 如果删除异步关键字,则必须修改对RunValidation()方法的调用。 I still need it to await, so this is what I did: 我仍然需要等待它,所以这就是我所做的:

private RelayCommand<Options> _executeCommand;

public RelayCommand<Options> ExecuteCommand
{
    get
    {                
        return _executeCommand ?? (_executeCommand = new RelayCommand<Options>((options) =>
        {
            Completed = false;                    

            var context = TaskScheduler.FromCurrentSynchronizationContext();

            var cancellationTokenSource = new CancellationTokenSource();

            Task.Run(async () => await RunValidation(options, cancellationTokenSource.Token));

            Completed = true;

        }));
    }
}

The problem with this code is that it doesn't await. 此代码的问题是它不会等待。 So I am at a loss. 所以我很茫然。

Can anyone shed some light on this for me please. 任何人都可以帮我澄清一下。 I've spend 2 plus days on this and I am still here. 我已经花了2天以上的时间,但我仍然在这里。

Thanks, Tim 谢谢,蒂姆

Here are the bindings to the Command Buttons. 这是命令按钮的绑定。

private readonly Independent<bool> _completed = new Independent<bool>(true);

public bool Completed
{
    get { return _completed; }
    set { _completed.Value = value; }
}

private ICommand _doneCommand;

public ICommand DoneCommand
{
    get
    {
        return _doneCommand ?? (_doneCommand = MakeCommand.When(() => Completed).Do(() =>
        {
            DoSomething();
        }));
    }
}

private ICommand _cancelCommand;

public ICommand CancelCommand
{
    get
    {
        return _cancelCommand ??
               (_cancelCommand = MakeCommand.When(() => !Completed).Do(() => DoSomthingElse()));
    }
}

I am using the MakeCommand objects from the UpdateControls library from Michael Perry. 我正在使用Michael Perry的UpdateControls库中的MakeCommand对象。 They contain dependancy tracking that raises the CanExecuteChange events when the Complete property is changed. 它们包含依赖项跟踪,当完成属性更改时,依赖项跟踪会引发CanExecuteChange事件。

Your first code is correct. 您的第一个代码是正确的。 Most likely you have an incorrect implementation for your Completed property. 很可能您的Completed属性实现不正确。 Your view model object should implement INotifyPropertyChanged . 您的视图模型对象应实现INotifyPropertyChanged The easiest way to do this right is use a base class that provides the functionality. 实现此权利的最简单方法是使用提供功能的基类。 ReactiveUI is the nuget package I always use. ReactiveUI是我一直使用的nuget包。 Usage is as simple as 用法很简单

public class MyObject : ReactiveObject {

    private bool _Completed;
    public bool Completed {
        get => _Completed;
        set => this.RaiseAndSetIfChanged(ref _Completed, value);
    }

}

This will make sure that notifications are raised to the UI when the property is changed. 这将确保在更改属性时向用户界面发出通知。

If you want it to be more magic you can use ReactiveUI.Fody and then your code will reduce to 如果您希望它更具魔力,可以使用ReactiveUI.Fody ,然后您的代码将减少为

public class MyObject : ReactiveObject {

    [Reactive]
    public bool Completed { get; set;}

}

So the issue was in fact the third party library. 因此,问题实际上是第三方库。 I was using it to provide dependency tracking for the 2 buttons. 我使用它来为2个按钮提供依赖项跟踪。 So when the complete flag changed it raised the CanExecuteChange events for both buttons without me having write code to do it. 因此,当完成标志更改时,它引发了两个按钮的CanExecuteChange事件,而我没有编写代码来执行此操作。 Unfortunately it stopped working after introducing the async/await calls. 不幸的是,在引入了异步/等待调用之后,它停止了工作。 I replaced the 2 MakeCommands with RelayCommands and raised the events myself and everything worked. 我用RelayCommands替换了2个MakeCommands,并自己引发了事件,并且一切正常。

So thanks to everyone for your responses. 因此,感谢大家的回应。

Tim 蒂姆

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM