从ViewModel构造函数Xamarin.Forms调用异步方法

Hel*_*ate 6 constructor asynchronous async-await xamarin xamarin.forms

我无法从az找到一个直接的示例来说明如何以安全的方式实现从构造函数调用异步方法。以下是我提出的内容,但是我不太了解这些概念,因此我不知道它是否真的正确。有人可以祝福这种格式吗?

创建IAsyncInitialization接口:

/// <summary>
/// The result of the asynchronous initialization of this instance.
/// see http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html
/// </summary>
Task Initialization { get; }
Run Code Online (Sandbox Code Playgroud)

然后在此ViewModel上拍打接口:

public GotoViewModel() // constructor
{
    Initialization = InitializeAsync();
}

public Task Initialization { get; private set; }
private async Task InitializeAsync()
{
    //call some async service and get data
}
Run Code Online (Sandbox Code Playgroud)

从使用此ViewModel的xaml.cs背后的代码中:

public partial class GotoPage : ContentPage, IAsyncInitialization
{
    IGotoViewModel VM;
    public GotoPage()
    {
         InitializeComponent();
         VM = App.Container.Resolve<IGotoViewModel>();
         Initialization = InitializeAsync();
     }

     public Task Initialization { get; private set; }

     private async Task InitializeAsync()
     {
          await VM.Initialization;
          this.BindingContext = VM;
     }
}
Run Code Online (Sandbox Code Playgroud)

这段代码很好用,但是我知道那并不重要。

Ada*_*ley 8

可能即将在c#上发布未来版本的功能是具有异步构造函数的功能,但是直到我们实现令人敬畏的未来之前,这里是您的选择。

您可以使用

.Wait()
Run Code Online (Sandbox Code Playgroud)

但是,在执行此操作时,您需要非常清楚正在运行的线程。如果在UI线程中调用了VM,则将锁定UI,直到异步任务完成。如果异步任务还在UI线程中使用某些东西,那么您将在此处陷入僵局。

一种缓解某些情况的方法是通过

Task.Run(async () => { await Initialize(); }).Wait();
Run Code Online (Sandbox Code Playgroud)

但是,它并非万无一失,可能会出现僵局。

另一个选择是做一些非常高级的事情,即使我也不完全理解这段代码在做什么,但是它确实起作用。

如果您查看Exrin ThreadHelper.cs,则其中包含方法

public static void RunOnUIThread(Func<Task> action)
Run Code Online (Sandbox Code Playgroud)

这使您可以在同一UIThread中运行任务,而不会阻止它。因为有时您需要在UI线程中运行异步任务,然后等待它。这是复杂的东西,但可能。

现在,您知道了为什么从构造函数执行异步操作确实非常棘手。这是简单的方法。

由于您的页面绑定到ViewModel,所以最好的方法是将OnAppearing方法中继给它。因此,在您的页面中

public async void OnAppearing()
{
    await MyViewModelInstance.OnAppearing();
}
Run Code Online (Sandbox Code Playgroud)

然后在您的ViewModel中,您可以执行

public async Task OnAppearing()
{
    await InitializeAsync();
}
Run Code Online (Sandbox Code Playgroud)

如果您确定调用该方法的人员不需要一个Task来等待事件完成,则可以完全接受async void。

这样,初始化将在视图出现而不是在其构造上运行。该方法适用于App.xaml.cs,您可以在OnStart而不是构造函数中执行此操作。

  • 我知道我们不应该说谢谢,但我已经阅读了您的几个博客。感谢您在社区中的活跃! (4认同)
  • 据我所知,当使用 async void 方法时,您需要确定它不会抛出异常,因为这样该异常就无处可冒,并且它将触发全局异常处理程序/使应用程序崩溃。我总是总是将我的 async void 方法包装在 try catch 中并优雅地处理任何意外异常(日志,如果处于调试模式则中断调试器等) (2认同)