我发现在将桌面程序转换为异步时重新考虑 UI 并不少见。与相应的同步程序相比,异步程序可以具有更多可能的状态。
有几个有用的模式:禁用控件(或者,等效地,用“正在加载...”符号覆盖它们),并保持状态“上下文”(我在下面解释)。您也可以维护一个操作队列,但大多数人不会打扰,因为构建一个用于管理操作队列的 UI 并不容易。
此操作本质上是不可阻挡的,例如数据库请求或文件下载。
(旁注:这些都不是天生不可阻挡的)
我们可以允许用户更改项目并开始新的操作。已经运行的操作的结果现在可以放弃或放入缓存。
这些都是合理的方法,具体取决于您的应用程序和操作类型。
现在让我们想象一下,用户再次点击 item1、item2 和 item1。当他相当快地执行此操作时,item1 的已启动且未取消的操作仍可能无法完成。在这种情况下,我们最好等待这个操作的结果,而不是开始一个新的。因此,必须存在一些当前正在执行的操作的缓存。
我不建议为病态的用户场景进行设计。在这种情况下,如果用户真的以惊人的速度点击该主列表,那么让他们忍受性能下降。如果你真的很在意,你可以缓存结果(就像你上面提到的那样),这样 item1 的重新执行就会非常快。结果的缓存应该足够了;您不需要执行操作的缓存。
当然,我们可以在操作完成之前禁用主项目列表,但是虽然 UI 的其他部分保持活动状态,但对用户来说并不是很友好。
要记住的一件事是,它与同步应用程序一样对用户不友好,同步应用程序(大概)在这段时间内没有响应。
每个列表的内容取决于所选的主条目。除此之外,列表相互影响的方式是,一个列表中的选择会改变另一个列表的状态。
您可能需要重新考虑您的 UI。你真的需要这么复杂的吗?换一种方式想一想:您使用的任何公开可用的应用程序是否具有如此复杂的 UI?他们会做什么呢?
也就是说,我在博客中解释了一个古老的异步编程技巧:异步回调上下文。本质上,这个想法是您使用“cookie”来定义“当前”状态(对于某些范围);当状态改变时,你改变“cookie”。属于该范围的所有异步方法都可以监视 cookie 并在其更改时采取特殊措施。
您可以使用object
cookie(正如我在博客中所描述的那样),但您也可以使用CancellationToken
:
private CancellationTokenSource masterListSelectionCookie;
private Task list1Download;
private Task list2Download;
void MasterList_Click(...)
{
// Change the cookie, canceling any previous one.
if (masterListSelectionCookie != null)
masterListSelectionCookie.Cancel();
masterListSelectionCookie = new CancellationTokenSource();
// Clear out both lists.
list1.Items.Clear();
list2.Items.Clear();
// Start both lists downloading.
list1Download = DownloadList1Async();
list2Download = DownloadList2Async();
}
async void List1_Click(...)
{
// Get a local copy of the current cookie.
var localMasterListSelectionCookie = masterListSelectionCookie.Token;
// Ensure list2 is done downloading.
await list2Download;
// If the cookie has changed, ignore the click.
if (localMasterListSelectionCookie.IsCancellationRequested)
return;
// Apply the click changes to list2 items.
FilterList2(list1.Item);
}
async void List2_Click(...)
{
// Get a local copy of the current cookie.
var localMasterListSelectionCookie = masterListSelectionCookie.Token;
// Ensure list1 is done downloading.
await list1Download;
// If the cookie has changed, ignore the click.
if (localMasterListSelectionCookie.IsCancellationRequested)
return;
// Apply the click changes to list1 items.
FilterList1(list2.Item);
}
这个想法是,对于每个对 cookie 中隐含的“状态”敏感的操作,它们会在每个操作之后检查await
cookie 是否已更改(CancellationToken
即已被取消)并采取适当的行为(在这种情况下,只是不过滤其他列表)。