Вызвать асинхронный метод в конструкторе?

резюме: я хотел бы вызвать асинхронный метод в конструкторе. Возможно ли это?

подробности: у меня есть метод, называемый getwritings() это анализирует данные JSON. Все работает нормально, если я просто позвоню getwritings() на async способ и поставить await слева от него. Однако , когда я создаю LongListView на моей странице и попытаться заполнить его, я нахожу, что getWritings() удивительно, возвращающихся null и LongListView is пустой.

чтобы решить эту проблему, я попытался изменить тип возврата getWritings() to Task<List<Writing>> и затем получение результата в конструкторе через getWritings().Result. Тем не менее, это заканчивается блокировки потока пользовательского интерфейса.

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

6 ответов


лучшим решением является признание асинхронного характера загрузки и проектирования для него.

другими словами, решите, как должно выглядеть ваше приложение во время загрузки данных. Настройте конструктор страницы это просмотр и начало загрузки. Когда загрузка завершится обновление страница для отображения данных.

У меня есть сообщение в блоге на асинхронный конструкторы что вы можете найти полезным. Также, некоторые статьи MSDN; один на асинхронная привязка данных (если вы используете MVVM) и другой на асинхронные рекомендации (т. е., вы должны избегать 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
    }
}

в основном, мы делаем конструктор частным и делаем наш собственный открытый статический асинхронный метод, который отвечает за создание экземпляра 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();

попробуйте заменить этот:

myLongList.ItemsSource = writings;

С

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

вы могли бы попробовать AsyncMVVM.

Страница 2.язык XAML:

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

Страница 2.код 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;
        }
    }
}

проще говоря, ссылаясь на Стивена Клири https://stackoverflow.com/a/23051370/267000

ваша страница при создании должна создавать задачи в конструкторе, и вы должны объявить эти задачи как члены класса или поместить его в пул задач.

ваши данные извлекаются во время этих задач, но эти задачи должны ожидаться в коде, т. е. на некоторых манипуляциях UI, т. е. Ok Click etc.

Я разработал такие приложения в WP, у нас была целая куча задачи, созданные при запуске.