webentwicklung-frage-antwort-db.com.de

Asynchrone Methode im Konstruktor aufrufen?

Summary: Ich möchte eine asynchrone Methode in einem Konstruktor aufrufen. Ist das möglich?

Details: Ich habe eine Methode namens getwritings(), die JSON-Daten analysiert. Alles funktioniert gut, wenn ich nur getwritings() in einer async -Methode aufrufe und await links davon setze. Wenn ich jedoch ein LongListView auf meiner Seite erstelle und versuche, es auszufüllen, stelle ich fest, dass getWritings() überraschenderweise null und das LongListView zurückgibt leeren.

Um dieses Problem zu beheben, habe ich versucht, den Rückgabetyp von getWritings() in Task<List<Writing>> Zu ändern und dann das Ergebnis im Konstruktor über getWritings().Result abzurufen. Dies führt jedoch dazu, dass der UI-Thread blockiert wird.

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

Die beste Lösung besteht darin, die Asynchronität des Downloads zu berücksichtigen und das Design dafür zu erstellen.

Mit anderen Worten, entscheiden Sie, wie Ihre Anwendung aussehen soll, während die Daten heruntergeladen werden. Lassen Sie den Seitenkonstruktor diese Ansicht einrichten und starten Sie den Download. Wenn der Download abgeschlossen ist , aktualisieren Sie die Seite, um die Daten anzuzeigen.

Ich habe einen Blog-Beitrag über asynchrone Konstruktoren , den Sie vielleicht nützlich finden. Auch einige MSDN-Artikel; Eine für asynchrone Datenbindung (wenn Sie MVVM verwenden) und eine für asynchrone bewährte Methoden (d. h. Sie sollten async void vermeiden).

104
Stephen Cleary

Sie können auch einfach so vorgehen:

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

Ich möchte ein Muster mitteilen, mit dem ich diese Art von Problemen gelöst habe. Es funktioniert ziemlich gut, denke ich. Natürlich funktioniert es nur, wenn Sie die Kontrolle darüber haben, was der Konstruktor aufruft. Beispiel unten

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

Grundsätzlich machen wir den Konstruktor privat und unsere eigene öffentliche statische asynchrone Methode, die für die Erstellung einer Instanz von MyClass verantwortlich ist. Indem wir den Konstruktor privat machen und die statische Methode in derselben Klasse belassen, haben wir sichergestellt, dass niemand "versehentlich" eine Instanz dieser Klasse erstellen kann, ohne die richtigen Initialisierungsmethoden aufzurufen. Die gesamte Logik rund um die Erstellung des Objekts ist weiterhin in der Klasse enthalten (nur in einer statischen Methode).

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

Auf das aktuelle Szenario implementiert würde es ungefähr so ​​aussehen:

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

Und anstatt zu tun

var page = new Page2();

Das würdest du tun

var page = await Page2.Create();
47
Shazi

Versuchen Sie dies zu ersetzen:

myLongList.ItemsSource = writings;

mit diesem

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

Einfach ausgedrückt, unter Bezugnahme auf Stephen Cleary https://stackoverflow.com/a/23051370/2670

ihre Seite bei der Erstellung sollte Aufgaben im Konstruktor erstellen und Sie sollten diese Aufgaben als Klassenmitglieder deklarieren oder in Ihren Aufgabenpool aufnehmen.

Ihre Daten werden während dieser Aufgaben abgerufen, aber diese Aufgaben sollten im Code abgewartet werden, d. H. Bei einigen UI-Manipulationen, d. H. Ok Click usw.

Ich habe solche Apps in WP entwickelt, wir hatten eine ganze Reihe von Aufgaben beim Start erstellt.

2
Dmitry Dyachkov

Sie könnten versuchen AsyncMVVM .

Seite 2.xaml:

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

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

Eine schnelle Möglichkeit, zeitaufwändige Vorgänge in einem Konstruktor auszuführen, besteht darin, eine Aktion zu erstellen und diese asynchron auszuführen.

new Action( async () => await getWritings())();

Wenn Sie diesen Code ausführen, wird weder Ihre Benutzeroberfläche blockiert, noch werden Sie mit losen Threads zurückgelassen. Wenn Sie eine Benutzeroberfläche aktualisieren müssen (sofern Sie nicht den MVVM-Ansatz verwenden), können Sie den Dispatcher verwenden, wie von vielen vorgeschlagen.

0
Anup Sharma