¿Llamar al método asincrónico en el constructor?

Resumen : me gustaría llamar a un método asincrónico en un constructor. es posible?

Detalles : Tengo un método llamado getwritings() que analiza datos JSON. Todo funciona bien si solo llamo a getwritings() en un método async y lo pongo a la izquierda. Sin embargo, cuando creo un LongListView en mi página y trato de poblarlo, me getWritings() que getWritings() sorprendentemente devuelve null y LongListView está vacío.

Para resolver este problema, traté de cambiar el tipo de retorno de getWritings() a la Task<List> y luego recuperar el resultado en el constructor a través de getWritings().Result . Sin embargo, hacer eso termina bloqueando el hilo de UI.

 public partial class Page2 : PhoneApplicationPage { List 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; } } 

La mejor solución es reconocer la naturaleza asíncrona de la descarga y el diseño para ella.

En otras palabras, decida cómo se verá su aplicación mientras se descargan los datos. Haga que el constructor de la página configure esa vista y comience la descarga. Cuando la descarga finalice, actualice la página para mostrar los datos.

Tengo una publicación de blog sobre constructores asincrónicos que puede serle útil. Además, algunos artículos de MSDN; uno sobre el enlace de datos asincrónico (si está utilizando MVVM) y otro sobre las mejores prácticas asincrónicas (es decir, debe evitar un async void ).

También puede hacer simplemente así:

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

Me gustaría compartir un patrón que he estado usando para resolver este tipo de problemas. Funciona bastante bien, creo. Por supuesto, solo funciona si tienes control sobre lo que llama el constructor. Ejemplo a continuación

 public class MyClass { public static async Task 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 } } 

Básicamente, lo que hacemos es hacer que el constructor sea privado y hacer nuestro propio método público asíncrono estático que es responsable de crear una instancia de MyClass. Al hacer que el constructor sea privado y mantener el método estático dentro de la misma clase, nos hemos asegurado de que nadie pueda crear “accidentalmente” una instancia de esta clase sin llamar a los métodos de inicialización adecuados. Toda la lógica alrededor de la creación del objeto todavía está contenida dentro de la clase (solo dentro de un método estático).

 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 

Implementado en el escenario actual se vería algo así como:

 public partial class Page2 : PhoneApplicationPage { public static async Task Create() { var page = new Page2(); await page.getWritings(); return page; } List 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; } } 

Y en lugar de hacer

 var page = new Page2(); 

Estarías haciendo

 var page = await Page2.Create(); 

Intenta reemplazar esto:

 myLongList.ItemsSource = writings; 

con este

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

Podrías probar AsyncMVVM .

Page2.xaml:

    

Page2.xaml.cs:

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

ViewModel2.cs:

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

Para decirlo simplemente, refiriéndose a Stephen Cleary https://stackoverflow.com/a/23051370/267000

su página de creación debe crear tareas en el constructor y debe declarar esas tareas como miembros de la clase o colocarla en su grupo de tareas.

Sus datos se obtienen durante estas tareas, pero estas tareas deben esperar en el código, es decir, en algunas manipulaciones de la interfaz de usuario, es decir, haga clic en Aceptar, etc.

Desarrollé tales aplicaciones en WP, tuvimos un montón de tareas creadas desde el principio.