总结:我想在构造函数中调用异步方法。这可能吗?

详细信息:我有一个名为getwritings()的方法,用于解析JSON数据。如果我只是在异步方法中调用getwritings()并将await放在它的左边,那么一切都可以正常工作。然而,当我在我的页面中创建一个LongListView并试图填充它时,我发现getWritings()令人惊讶地返回null, LongListView为空。

为了解决这个问题,我尝试将getWritings()的返回类型更改为Task<List<Writing>>,然后通过getWritings(). result在构造函数中检索结果。然而,这样做最终会阻塞UI线程。

public partial class Page2 : PhoneApplicationPage
{
    List<Writing> writings;

    public Page2()
    {
        InitializeComponent();
        getWritings();
    }

    private async void getWritings()
    {
        string jsonData = await JsonDataManager.GetJsonAsync("1");
        JObject obj = JObject.Parse(jsonData);
        JArray array = (JArray)obj["posts"];

        for (int i = 0; i < array.Count; i++)
        {
            Writing writing = new Writing();
            writing.content = JsonDataManager.JsonParse(array, i, "content");
            writing.date = JsonDataManager.JsonParse(array, i, "date");
            writing.image = JsonDataManager.JsonParse(array, i, "url");
            writing.summary = JsonDataManager.JsonParse(array, i, "excerpt");
            writing.title = JsonDataManager.JsonParse(array, i, "title");

            writings.Add(writing);
        }

        myLongList.ItemsSource = writings;
    }
}

当前回答

最好的解决方案是承认下载的异步性质,并为此进行设计。

换句话说,决定在数据下载时应用程序应该是什么样子。让页面构造函数设置该视图,并开始下载。下载完成后更新页面以显示数据。

我有一篇关于异步构造函数的博客文章,您可能会觉得有用。还有一些MSDN文章;一个是关于异步数据绑定的(如果您使用MVVM),另一个是关于异步最佳实践的(即,您应该避免异步void)。

其他回答

在任何构造函数中执行一些耗时操作的快速方法是创建一个动作并异步运行它们。

new Action( async() => await InitializeThingsAsync())();

运行这段代码既不会阻塞UI,也不会留下任何松散的线程。如果您需要更新任何UI(考虑到您没有使用MVVM方法),您可以像许多人建议的那样使用Dispatcher进行更新。

注意:如果你没有任何init或onload或导航覆盖,这个选项只提供了一种从构造函数开始执行方法的方法。最有可能的是,即使在建设完成后,它仍将继续运行。因此,此方法调用的结果可能在构造函数本身中不可用。

试着替换一下:

myLongList.ItemsSource = writings;

用这个

Dispatcher.BeginInvoke(() => myLongList.ItemsSource = writings);

不要调用. wait()或. result,因为这将锁定你的应用程序。 也不要启动一个新的Task,只需调用ContinueWith

public class myClass
{
  public myClass
  {
    GetMessageAsync.ContinueWith(GetResultAsync);
  }

  async Task<string> GetMessageAsync()
  {
    return await Service.GetMessageFromAPI(); 
  }
private async Task GetResultAsync(Task<string> resultTask)
{
    if (resultTask.IsFaulted)
{
      Log(resultTask.Exception); 
}
eles
{
 //do what ever you need from the result
}
}
}

https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern

我想分享一个我一直用来解决这类问题的模式。我认为它运行得相当好。当然,只有当您能够控制调用构造函数的对象时,它才能工作。

public class MyClass
{
    public static async Task<MyClass> Create()
    {
        var myClass = new MyClass();
        await myClass.Initialize();
        return myClass;
    }

    private MyClass()
    {
    }

    private async Task Initialize()
    {
        await Task.Delay(1000); // Do whatever asynchronous work you need to do
    }
}

基本上,我们所做的就是将构造函数设为私有并将自己的公共静态异步方法设为负责创建MyClass实例的静态异步方法。通过将构造函数设置为私有并将静态方法保留在同一个类中,我们可以确保没有人可以在没有调用适当的初始化方法的情况下“意外地”创建该类的实例。

围绕对象创建的所有逻辑仍然包含在类中(只是在静态方法中)。

var myClass1 = new MyClass() // Cannot be done, the constructor is private
var myClass2 = MyClass.Create() // Returns a Task that promises an instance of MyClass once it's finished
var myClass3 = await MyClass.Create() // asynchronously creates and initializes an instance of MyClass

在当前场景中实现,它看起来像这样:

public partial class Page2 : PhoneApplicationPage
{
    public static async Task<Page2> Create()
    {
        var page = new Page2();
        await page.getWritings();
        return page;
    }

    List<Writing> writings;

    private Page2()
    {
        InitializeComponent();
    }

    private async Task getWritings()
    {
        string jsonData = await JsonDataManager.GetJsonAsync("1");
        JObject obj = JObject.Parse(jsonData);
        JArray array = (JArray)obj["posts"];

        for (int i = 0; i < array.Count; i++)
        {
            Writing writing = new Writing();
            writing.content = JsonDataManager.JsonParse(array, i, "content");
            writing.date = JsonDataManager.JsonParse(array, i, "date");
            writing.image = JsonDataManager.JsonParse(array, i, "url");
            writing.summary = JsonDataManager.JsonParse(array, i, "excerpt");
            writing.title = JsonDataManager.JsonParse(array, i, "title");

            writings.Add(writing);
        }

        myLongList.ItemsSource = writings;
    }
}

而不是做

var page = new Page2();

你可以使用:

var page = await Page2.Create();

我喜欢的方法是:

// caution: fire and forget
Task.Run(async () => await someAsyncFunc());