2017-04-13 257 views
2

我遇到了一些与任务和异步/等待关键字混淆。我知道你不应该混用异步和阻止代码。或者至少我对混合它们的解释是:等待/异步任务不等待

不要调用阻塞来自非异步方法的API。所以这是我的问题。

我想等待一个方法,然后相应地更新用户界面。问题在于等待异步方法()调用的唯一方法是从async方法和async方法中调用。

下面是一个例子:

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; 

     })); 
    } 
} 

此代码正常运行的方法和等待。问题出在我回来的时候。出于某种原因,设置完成标志时,取决于此标志的按钮不会切换。如果我评论等待代码,那么按钮将正确切换。所以,假设我没有返回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; 

     })); 
    } 
} 

这里是RunValidation()方法:

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

如果你注意到,在ExecuteCommand具有异步在传递给命令的(选项)参数之前的关键字。如果我删除了异步关键字,那么我必须修改对RunValidation()方法的调用。我仍然需要它等待,所以这就是我所做的:

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; 

     })); 
    } 
} 

此代码的问题是它不会等待。所以我很茫然。

任何人都可以在这里为我阐明一些东西。我已经花了两天多的时间,而我仍然在这里。

感谢, 添

这里是绑定命令按钮。

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())); 
    } 
} 

我使用来自Michael Perry的UpdateControls库中的MakeCommand对象。它们包含依赖性跟踪,在完成属性更改时引发CanExecuteChange事件。

+2

第一个代码块是正确的。这些按钮如何绑定到完成标志?它是否可观察? – Jason

+0

由于您的问题是由于通知丢失,因为你正在使用应提高您通知您一个图书馆,我建议你提出问题与UpdateControls库。 –

+0

所以我不疯狂,从事物的外观,我正确地做事情? – user953710

回答

0

你的第一个代码是正确的。很有可能你的Completed属性有错误的实现。您的视图模型对象应该实现INotifyPropertyChanged。最简单的方法是使用提供功能的基类。 ReactiveUI是我一直使用的nuget包。使用非常简单

public class MyObject : ReactiveObject { 

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

} 

这将确保在属性发生更改时向用户界面引发通知。

如果你想让它更神奇,你可以使用ReactiveUI.Fody,然后你的代码将减少

public class MyObject : ReactiveObject { 

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

} 
0

所以这个问题,其实是第三方库。我使用它来为2个按钮提供依赖关系跟踪。所以当完整的标志改变后,它为两个按钮提出了CanExecuteChange事件,而我没有写代码去做。不幸的是,在引入异步/等待呼叫后,它停止工作。我用RelayCommands替换了2个MakeCommands,并且自己提出了事件并且一切正常。

所以感谢大家您的答复。

Tim