it-swarm.com.ru

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

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

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

Чтобы решить эту проблему, я попытался изменить тип возвращаемого значения 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;
    }
}
144
Kaan Baris Bayrak

Лучшее решение - признать асинхронный характер загрузки и дизайн для нее.

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

У меня есть пост в блоге асинхронные конструкторы , который может оказаться полезным. Кроме того, некоторые статьи MSDN; один на асинхронное связывание данных (если вы используете MVVM), а другой на асинхронный передовой опыт (т.е. вы должны избегать async void).

96
Stephen Cleary

Вы также можете сделать так:

Task.Run(() => this.FunctionAsync()).Wait();
65
Peter Stegnar

Я хотел бы поделиться моделью, которую я использовал для решения подобных проблем. Это работает довольно хорошо, я думаю. Конечно, это работает, только если у вас есть контроль над тем, что вызывает конструктор. Пример ниже

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

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

myLongList.ItemsSource = writings;

с этим

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

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

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

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

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

2
Dmitry Dyachkov

Вы можете попробовать 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;
        }
    }
}
1
Dmitry Shechtman