在构造函数中调用asynchronous方法?

简介 :我想在构造函数中调用asynchronous方法。 这可能吗?

详细信息 :我有一个名为getwritings()分析JSON数据的方法。 一切正常,如果我只是在async方法中调用getwritings() ,并await它的左侧。 然而,当我在我的页面中创build一个LongListView并尝试填充它时,我发现getWritings()出人意料地返回null ,而LongListView是空的。

为了解决这个问题,我试着将getWritings()的返回types改为Task<List<Writing>> ,然后通过getWritings().Result在构造函数中检索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; } } 

最好的解决办法是承认下载和devise的asynchronous性质。

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

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

你也可以这样做:

 Task.Run(() => this.FunctionAsync()).Wait(); 

我想分享一个我一直用来解决这类问题的模式。 我觉得它工作得很好。 当然,只有当你能够控制构造函数的调用时,它才有效。 下面的例子

 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 } } 

基本上我们做的是我们使构造函数是私有的,并且使我们自己的公共静态asynchronous方法负责创build一个MyClass的实例。 通过使构造函数保持私有状态并将静态方法保留在同一个类中,我们确保没有人可以“不经意”地创build这个类的实例,而无需调用正确的初始化方法。 围绕对象创build的所有逻辑仍然包含在类中(只是在一个静态方法中)。

 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(); 

尝试replace这个:

 myLongList.ItemsSource = writings; 

有了这个

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

你可以试试AsyncMVVM 。

Page2.xaml:

 <PhoneApplicationPage x:Class="Page2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <ListView ItemsSource="{Binding Writings}" /> </PhoneApplicationPage> 

Page2.xaml.cs:

 public partial class Page2 { InitializeComponent(); DataContext = new ViewModel2(); } 

ViewModel2.cs:

 public class ViewModel2: AsyncBindableBase { public IEnumerable<Writing> Writings { get { return Property.Get(GetWritingsAsync); } } private async Task<IEnumerable<Writing>> GetWritingsAsync() { 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"); yield return writing; } } } 

简单来说,请参阅Stephen Cleary https://stackoverflow.com/a/23051370/267000

你的创build页面应该在构造函数中创build任务,你应该将这些任务声明为类成员,或者把它放在你的任务池中。

您的数据在这些任务中被提取,但是这些任务应该在代码中等待,例如在一些UI操作上,即Ok点击等等。

我在WP开发了这样的应用程序,我们在开始时创build了大量的任务。