我试图澄清我对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);
}
所以现在我的问题是:
GetPatientList()
在PatientList
需要时尚未完成(GetPatientListFromName(...)
未知),GetPatientListFromName(...)
会在继续进行之前自动等待其完成LINQ表达式?或者更需要什么?PatientList = await GetPatientList();
,GetPatientList()
会再次启动 - 即使它已经在运行了吗?答案 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
开始之前调用await
。PatientList
在await
完成后分配。
醇>
考虑到这一点,您可以回答您的问题:
如果在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 Task
。 It's a best practice to avoid async void