使用MVVM模式实现异步“loadData”方法的最佳方式

10

我正在尝试理解在调用更新ViewModel的异步方法时,最好的方法是什么。 现在,假设我有像这样的东西:

视图:

private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
    //Call my ViewModel method to update the data the UI is bound to          
}

视图模型:

public async Task loadData()
{
    this.Source = await loadStuffFromDatabaseAsync();
}

现在,我不确定应该使用以下哪种方法:

1) 在我的LoadState方法中使用:

await Task.Run(async () => { await ViewMode.loadData(); });

2) 使用 Task.Run 调用 loadData 方法,不需要在 Action 中等待:

await Task.Run(() => { ViewModel.loadData(); });

3) 使用以下内容调用我的loadData方法:

await ViewModel.loadData().ConfigureAwait(false);

4) 在我的 View 类中,不等待调用 loadData 方法,并在 loadData 方法内部使用 Task.Run:

View:

private void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
    ViewModel.loadData();  
}

ViewModel:

public async void loadData()
{
    await Task.Run(async () => 
    {
        this.Source = await loadStuffFromDatabaseAsync();
    });
}

这些方法之间的主要区别是什么?

其中一个比另一个更有效,我应该选择其中一个吗?

感谢您的帮助!:)

Sergio


应避免使用async void方法,并且您应该遵循将Async附加到所有异步方法的命名约定。 - Philip Stuyck
我相信Reed Cosby教给我们的是,现在既然异步支持已经集成到基本代码中,我们只需要两个词Async和Await。我们不再需要调用Task方法了。 - JWP
2
@JohnPeters:那不是真的。可能有阻塞UI线程的代码。 - Fred
@PhilipStuyck 除了他示例中提到的顶层事件处理程序以外。 - fex
@JohnPeters 没问题,老兄 :) - Sergio0694
显示剩余5条评论
1个回答

3

如果您有需要将 CPU 绑定或阻塞的工作移出 UI 线程,则应仅使用 Task.Run。但这里不是这种情况,因此直接调用(选项 3)最自然。

分别进行如下操作:

await Task.Run(async () => { await ViewMode.loadData(); });

该选项将在线程池线程上执行loadData。这可能不是很好,因为loadData正在更新UI(间接地通过设置VM属性)。即使它恰巧有效(即某些MVVM框架可以在某些情况下正确处理来自后台线程的更新),它也可能是不必要的,因为loadData是异步方法。
此外,这会无缘无故增加async状态机开销。
await Task.Run(() => { ViewModel.loadData(); });

这个选项有着和之前相同的问题,只是在没有async状态机开销的情况下,它略微更加高效。但它仍然会在后台线程上更新VM属性,并且没有必要地使用了后台线程。

public async void loadData()

这是最糟糕的一种方法。它继承了其他方法的问题:在后台线程上更新VM属性和使用不必要的后台线程。此外,它还增加了async void的问题。其中一个问题是NavigationHelper_LoadState无法捕获loadData中的任何异常。另一个问题是loadData不易于测试。

因此,只需使用简单的方法并直接调用它:

await ViewModel.loadData().ConfigureAwait(false);

谢谢你的回答!我只是有一个疑问:你说我应该仅在 CPU 密集型或阻塞工作时使用 Task.Run:为什么从数据库加载数据不算阻塞工作呢?由于它是 IO 操作,可能需要很长时间才能完成,因此会阻塞等待该任务的调用方法吗? - Sergio0694
1
线程没有被阻塞,因为它是一个异步操作。 - Stephen Cleary
你说得对,通过“阻塞工作”,你指的是运行一些同步代码。再次感谢你的帮助 :) - Sergio0694

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接