373 Stimmen

Warum async und return await verwenden, wenn Sie direkt Task<T> zurückgeben können?

Gibt es irgend ein Szenario, in dem das Schreiben einer Methode wie dieser Sinn macht:

public async Task DoSomethingAsync()
{
    // Es könnte hier synchroner Code stehen oder auch nicht... //
    return await DoAnotherThingAsync();
}

anstatt dieses:

public Task DoSomethingAsync()
{
    // Es könnte hier synchroner Code stehen oder auch nicht... //
    return DoAnotherThingAsync();
}

würde Sinn ergeben?

Warum die return await Konstruktion nutzen, wenn man das Task direkt von der inneren DoAnotherThingAsync() Aufruf zurückgeben kann?

Ich sehe überall Code mit return await, ich denke, ich habe etwas übersehen. Aber soweit ich verstehe, wäre es funktional äquivalent, in diesem Fall die async/await Schlüsselwörter nicht zu verwenden und den Task direkt zurückzugeben. Warum zusätzlichen Overhead durch eine weitere await Schicht hinzufügen?

12voto

Andrew Arnott Punkte 77359

Das ansonsten einfache "thunk" -Methode async macht eine async State Machine im Speicher, während die nicht-async keine erstellt. Obwohl das oft darauf hinweisen kann, dass Leute die nicht-async Version verwenden, weil sie effizienter ist (was stimmt), bedeutet dies auch, dass im Falle eines Hängens kein Hinweis darauf besteht, dass diese Methode im "return/continuation stack" enthalten ist, was es manchmal schwieriger macht, den Hänger zu verstehen.

Also ja, wenn die Leistung nicht entscheidend ist (und das ist sie normalerweise nicht), werde ich async auf all diese thunk Methoden werfen, damit ich die async State Machine habe, um mir später beim Diagnostizieren von Hängen zu helfen, und auch um sicherzustellen, dass, wenn sich diese thunk Methoden im Laufe der Zeit weiterentwickeln, sie sicher fehlgeschlagene Tasks zurückgeben werden anstatt zu werfen.

5voto

heltonbiker Punkte 24989

Dies verwirrt mich auch und ich habe das Gefühl, dass die vorherigen Antworten Ihre tatsächliche Frage übersehen haben:

Warum die return await-Konstruktion verwenden, wenn Sie das Task direkt aus dem inneren Aufruf von DoAnotherThingAsync() zurückgeben können?

Nun manchmal möchten Sie tatsächlich ein Task, aber die meiste Zeit möchten Sie eigentlich eine Instanz von SomeType, das heißt, das Ergebnis aus dem Task.

Aus Ihrem Code:

async Task DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return await foo.DoAnotherThingAsync();
    }
}

Ein Person, die nicht mit der Syntax vertraut ist (ich zum Beispiel), könnte denken, dass diese Methode ein Task zurückgeben sollte, aber da sie mit async markiert ist, bedeutet dies, dass ihr tatsächlicher Rückgabetyp SomeResult ist. Wenn Sie einfach return foo.DoAnotherThingAsync() verwenden, würden Sie ein Task zurückgeben, was nicht kompilieren würde. Der richtige Weg ist das Zurückgeben des Ergebnisses des Tasks, also das return await.

3voto

Sedat Kapanoglu Punkte 44545

Ein weiterer Grund, warum Sie möglicherweise return await verwenden möchten: Die await-Syntax ermöglicht es Ihnen, einen Missmatch zwischen den Typen Task und ValueTask zu vermeiden. Zum Beispiel funktioniert der unten stehende Code, obwohl die SubTask-Methode Task zurückgibt, der Aufrufer jedoch ValueTask zurückgibt.

async Task SubTask()
{
...
}

async ValueTask DoSomething()
{
  await UnimportantTask();
  return await SubTask();
}

Wenn Sie das await in der Zeile DoSomething() überspringen, erhalten Sie einen Compilerfehler CS0029:

Kann den Typ 'System.Threading.Tasks.Task' nicht implizit in 'System.Threading.Tasks.ValueTask' konvertieren.

Sie erhalten auch CS0030, wenn Sie versuchen, es explizit zu typisieren.

Das ist übrigens das .NET Framework. Ich kann absolut voraussehen, dass jemand kommentiert, dass dies in .NET hypothetische_version behoben ist, aber das habe ich nicht getestet. :)

1voto

Luke Vo Punkte 14825

Ein weiteres Problem bei nicht-await-Methoden ist manchmal, dass Sie den Rückgabetyp nicht implizit konvertieren können, insbesondere bei Task>:

async Task> GetListAsync(string foo) => new();

// Diese Methode funktioniert
async Task> GetMyList() => await GetListAsync("myFoo");

// Diese funktioniert nicht
Task> GetMyListNoAsync() => GetListAsync("myFoo");

Der Fehler:

Kann den Typ 'System.Threading.Tasks.Task' nicht implizit konvertieren in 'System.Threading.Tasks.Task'

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X