WPF如何处理等待Async / Await响应的字段?

时间:2016-06-10 19:46:19

标签: c# wpf asynchronous

我试图澄清我对Async / Await的理解。 (我在这里已经阅读了很多内容,但尚未明确掌握)。假设我有一个窗口构造函数,它开始了一个漫长的过程:

// Constructor
public MainWindowViewModel() : base()
{
    init();
   ........
}

private async void init()
{
    PatientList = await GetPatientList();   <--- A VERY LONG PROCESS
    .....
}

private ObservableCollection<ViewPatient> PatientList;

private async Task<ObservableCollection<ViewPatient>> GetPatientList()
{
    return new ObservableCollection<ViewPatient>(await MedicalClient.GetAllPatientsAsync());
}

所以,如果我做对了,等待将启动GetPatientList()方法,然后 立即返回呼叫者(MainWindowViewModel(),然后完成并显示窗口。

现在,在窗口中,我在列表中的名称上有一个搜索按钮。经过几次快速跳转后,取景器调用:

private async Task<ObservableCollection<ViewPatient>> GetPatientListFromName(string lastname, string firstname, string birthdate)
{
    string birth = string.Empty;
    if (!string.IsNullOrWhiteSpace(birthdate))
    {
        // regex to look for pattern: 00/00/0000, 0/00/0000, 00/0/0000
        Regex regex = new Regex(@"^\d{1,2}\/\d{1,2}\/\d{4}$");
        Match x = regex.Match(birthdate);
        if (!x.Success) return null;
        birth = birthdate;
    }

    string last = lastname ?? string.Empty;
    string first = firstname ?? string.Empty;

   // PatientList = await GetPatientList(); <--Is This Needed?

    var z = PatientList.Where(p =>
       p.Lastname.StartsWith(last.ToUpper()) &&
       p.Firstname.StartsWith(first.ToUpper()) &&
       ((DateTime)p.Birthdate).ToShortDateString().StartsWith(birth));

    return new ObservableCollection<ViewPatient>(z);

}

所以现在我的问题是:

  1. 如果GetPatientList()PatientList需要时尚未完成(GetPatientListFromName(...)未知),GetPatientListFromName(...)会在继续进行之前自动等待其完成LINQ表达式?或者更需要什么?
  2. 如果我添加(注释掉)PatientList = await GetPatientList();GetPatientList()会再次启动 - 即使它已经在运行了吗?

1 个答案:

答案 0 :(得分:4)

  

我试图澄清我对Async / Await的理解。

我建议你从my tutorial开始,它有(IMO)链接到底部的最佳后续资源。既然您正在编写WPF应用程序,我还会推荐关于异步MVVM的文章系列(特别是async data binding上的那篇)。对Q&amp; A来说非常棒,但它并不是真正意义上的教学/学习网站。

关于await的第一件事是它如何拆分它的async方法。特别是:

private async void init()
{
  PatientList = await GetPatientList();
}

基本上与此相同:

private async void init()
{
  var task = GetPatientList();
  var result = await task;
  PatientList = result;
}

这应该清楚

    GetPatientList开始之前调用
  1. await
  2. {li> PatientListawait完成后分配。

    考虑到这一点,您可以回答您的问题:

      

    如果在GetPatientListFromName(...)需要时GetPatientList()尚未完成(因此不知道PatientList),GetPatientListFromName(...)会在继续进入linq表达式之前自动等待其完成吗?

    没有。在PatientList完成null之前,await将为init

      

    如果我包括(注释掉)PatientList = await GetPatientList();将GetPatientList()再次启动 - 即使它已经在运行?

    是。这将再次调用GetPatientList,创建另一个任务。

    要解决您的实际问题(即只加载PatientList一次但允许搜索按钮正常工作),您可以通过几种不同的方式执行此操作。

    一种方法是保存“初始化”任务,如下:

    private Task _initTask;
    public MainWindowViewModel() : base()
    {
      _initTask = InitAsync();
      ...
    }
    
    private async Task InitAsync()
    {
      PatientList = await GetPatientList();
      ...
    }
    
    private async Task<ObservableCollection<ViewPatient>> GetPatientListFromName(string lastname, string firstname, string birthdate)
    {
      ...
      string last = lastname ?? string.Empty;
      string first = firstname ?? string.Empty;
    
      // Ensure PatientList is loaded.
      await _initTask;
    
      var z = PatientList.Where...
    }
    

    这将导致Search按钮(异步)等待PatientList(如果尚未完成)。对于已完成的await任务以及多次await个任务,它是完全安全的。

    这种方法的另一个好处是async void转换为async TaskIt's a best practice to avoid async void