简体   繁体   English

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

[英]How does WPF treat a field awaiting a response from Async/Await?

I'm trying to clarify my understanding of Async/Await. 我试图阐明我对异步/等待的理解。 (I have read much here on SO, but haven't gotten a clear grasp as yet). (我在这里已经阅读了很多关于SO的内容,但是目前还没有清楚的了解)。 Lets say I have a window constructor that begins a long process: 可以说我有一个开始很长时间的窗口构造函数:

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

So, if I get this right, await will start the GetPatientList() method, and then immediately return to the caller (the MainWindowViewModel() which will then complete and display the window. 因此,如果我做对了,await将启动GetPatientList()方法,然后立即返回到调用方( MainWindowViewModel() ,它将完成并显示窗口。

Now, in the window I have a search button on the names in the list. 现在,在窗口中,我在列表中的名称上有一个搜索按钮。 After a few quick jumps, the finder calls: 快速跳转后,查找器将调用:

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

}

So now my questions are: 所以现在我的问题是:

  1. If the GetPatientList() has not completed (so PatientList is not known) by the time GetPatientListFromName(...) needs it, will GetPatientListFromName(...) automatically wait for its completion prior to continuing into the LINQ expression? 如果GetPatientList()尚未完成(所以PatientList的时候不知道) GetPatientListFromName(...)需要的话,会GetPatientListFromName(...)自动之前持续到LINQ表达等待其完成? Or is something more needed? 还是需要更多东西?
  2. If I include the (commented out) PatientList = await GetPatientList(); 如果我包括(注释掉) PatientList = await GetPatientList(); will GetPatientList() be started again --even if it is already running? 会重新启动GetPatientList()即使它已经在运行了吗?

I'm trying to clarify my understanding of Async/Await. 我试图阐明我对异步/等待的理解。

I suggest that you start off with my tutorial , which has links to (IMO) the best follow-up resources at the bottom. 我建议您从本教程开始,该教程的底部链接至(IMO)最佳后续资源。 Since you're writing a WPF app, I would also recommend my article series on async MVVM (particularly the one on async data binding ). 由于您正在编写WPF应用程序,因此我也建议您阅读有关异步MVVM的文章系列(尤其是有关异步数据绑定的文章 )。 SO is great for Q&A, but it's not really meant to be a tutorial/learning site. SO对于Q&A非常有用,但这并不是要成为教程/学习网站。

The first thing to realize about await is how it splits up its async method. 关于await的第一件事是如何拆分其async方法。 In particular, this: 特别是:

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

is essentially the same as this: 基本上与此相同:

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

This should make it clear that 这应该清楚说明

  1. GetPatientList is invoked before the await begins. await开始之前调用GetPatientList
  2. PatientList is assigned after the await completes. await完成后分配PatientList

With that in mind, you can answer your questions: 考虑到这一点,您可以回答您的问题:

If the GetPatientList() has not completed (so PatientList is not known) by the time GetPatientListFromName(...) needs it, will GetPatientListFromName(...) automatically wait for its completion prior to continuing into the linq expression? 如果在需要GetPatientListFromName(...)时尚未完成GetPatientList()(因此不知道PatientList),GetPatientListFromName(...)将自动等待其完成,然后继续进入linq表达式吗?

No. The PatientList will be null until the await in init is completed. 不会。在完成init await之前, PatientList将为null

If I include the (commented out) PatientList = await GetPatientList(); 如果我包括(注释掉)PatientList = await GetPatientList(); will GetPatientList() be started again --even if it is already running? 会重新启动GetPatientList()-即使它已经在运行了吗?

Yes. 是。 This will invoke GetPatientList a second time, creating another task. 这将第二次调用GetPatientList ,从而创建另一个任务。


To solve your actual problem (that is, only load PatientList once but allow the search button to work appropriately), you can do this a few different ways. 要解决您的实际问题(即,仅加载一次PatientList ,但允许搜索按钮正常工作),您可以采用几种不同的方法来实现。

One approach is to save an "initialize" task, as such: 一种方法是保存 “初始化”任务,如下所示:

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...
}

This will cause your Search button to (asynchronously) wait for PatientList if it is not already complete. 如果尚未完成,这将导致您的“ Search按钮(异步)等待PatientList It's perfectly safe to await tasks that are already completed, and also to await tasks multiple times. 这是完全安全的, await那些已经完成的任务,并且还要await多次任务。

A nice side benefit of this approach is that the async void is transformed to async Task . 这种方法的一个不错的好处是将async void转换为async Task It's a best practice to avoid async void . 避免async void的最佳做法是

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

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