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



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



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



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

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

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

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

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

Comments

    Ничего не найдено.