在构造函数中调用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了大量的任务。