Asynchrone Programmierung: Futures
Inhalt
Was ist wichtig:
- Der Code in Dart wird in einer einzelnen Thread-Ausführung ( Note Thread - Thread ) ausgeführt.
- Aufgrund von Code, der den Thread für eine lange Zeit benötigt (blockiert), kann das Programm einfrieren.
Future
( futures
) Objekte stellen die Ergebnisse asynchroner Operationen dar - Verarbeitung oder E / A, die später abgeschlossen werden.- Um die Ausführung in Zukunft bis zum Abschluss auszusetzen, verwenden Sie
await
in der asynchronen Funktion (oder then()
wenn Sie die Future
API verwenden). - Verwenden Sie zum Abfangen von Fehlern das
try-catch
Konstrukt (oder catchError()
bei Verwendung der Future
API) in der asynchronen Funktion. - Erstellen Sie für die gleichzeitige Verarbeitung ein Isolat (oder einen Worker für die Webanwendung).
Der Code in Dart wird in einem einzelnen Ausführungsthread ausgeführt. Wenn der Code mit langen Berechnungen beschäftigt ist oder auf eine E / A-Operation wartet, wird das gesamte Programm angehalten.
Durch asynchrone Vorgänge kann Ihr Programm andere Aufgaben ausführen, während Sie auf den Abschluss des Vorgangs warten. Dart verwendet futures
, um die Ergebnisse asynchroner Operationen darzustellen. Sie können auch async and await oder die Future API verwenden, um mit futures
zu arbeiten.
Eine Notiz
Der gesamte Code wird im Kontext des Isolats ausgeführt, dem der gesamte vom Code verwendete Speicher gehört. Es kann nicht mehr als eine Codeausführung im selben Isolat gestartet werden.
Für die parallele Ausführung von Codeblöcken können Sie diese in separate Isolate aufteilen. (Webanwendungen verwenden Worker anstelle von Isolaten.) In der Regel wird jedes der Isolate auf einem eigenen Prozessorkern ausgeführt. Isolate teilen sich nicht den Speicher und können nur interagieren, indem sie sich gegenseitig Nachrichten senden. Informationen zum Thema finden Sie in der Dokumentation zu Isolaten oder Arbeitern .
Einführung
Schauen wir uns ein Beispiel für Code an, der die Programmausführung „einfrieren“ kann:
Unser Programm liest Nachrichten aus der Tagesdatei, zeigt sie an und zeigt dann Informationen an, die für den Benutzer immer noch von Interesse sind:
<gathered news goes here> Winning lotto numbers: [23, 63, 87, 26, 2] Tomorrow's forecast: 70F, sunny. Baseball score: Red Sox 10, Yankees 0
In diesem Beispiel besteht das Problem darin, dass alle Vorgänge nach dem Aufruf von gatherNewsReports()
warten, bis gatherNewsReports()
den Inhalt der Datei gatherNewsReports()
, unabhängig davon, wie lange es dauert. Wenn das Lesen der Datei lange dauert, muss der Benutzer auf die Ergebnisse der Lotterie, die Wettervorhersage und den Gewinner eines kürzlich durchgeführten Spiels warten.
Um die Reaktionsfähigkeit der Anwendung aufrechtzuerhalten, verwenden Dart-Autoren ein asynchrones Modell, um Funktionen zu identifizieren, die möglicherweise teure Arbeiten ausführen. Solche Funktionen geben ihren Wert über futures
.
Was ist die Zukunft?
future
ist eine Instanz der Future <T> -Klasse, bei der es sich um eine asynchrone Operation handelt, die ein Ergebnis vom Typ T zurückgibt. Wenn das Ergebnis der Operation nicht verwendet wird, wird der Future<void>
Typ durch Future<void>
. Beim Aufrufen einer Funktion, die die future
zurückgibt, passieren zwei Dinge:
- Die Funktion steht zur Ausführung in der Warteschlange und gibt ein unvollständiges
Future
Objekt zurück. - Später, wenn der Vorgang abgeschlossen ist, wird die
future
mit einem Wert oder Fehler beendet.
Um future
Code zu schreiben, haben Sie zwei Möglichkeiten:
- Verwenden Sie
async
- await
- Verwenden Sie die
Future
API
Async - warte
Die Schlüsselwörter async
und await
sind Teil der async
Unterstützung von Dart. Mit ihnen können Sie asynchronen Code schreiben, der wie synchroner Code aussieht und die Future
API nicht verwendet. Eine asynchrone Funktion ist eine Funktion mit dem Schlüsselwort async
vor ihrem Hauptteil. Das Schlüsselwort await
funktioniert nur in asynchronen Funktionen.
Hinweis: In Dart 1.x verzögern asynchrone Funktionen sofort die Ausführung. In Dart 2 werden asynchrone Funktionen nicht sofort angehalten, sondern synchron ausgeführt, bis das erste await
oder return
.
Der folgende Code simuliert das Lesen von Nachrichten aus einer Datei mit async
- await
. Öffnen Sie DartPad mit der Anwendung , starten Sie und klicken Sie auf KONSOLE, um das Ergebnis anzuzeigen.
Beachten Sie, dass wir zuerst printDailyNewsDigest()
aufrufen, die Nachrichten jedoch zuletzt gedruckt werden, selbst wenn die Datei nur eine Zeile enthält. Dies liegt daran, dass der Code, der die Datei liest und druckt, asynchron ausgeführt wird.
In diesem Beispiel printDailyNewsDigest()
gatherNewsReports()
, das nicht blockiert. Durch Aufrufen der Methode gatherNewsReports()
wird gatherNewsReports()
Job in die gatherNewsReports()
Ausführung des restlichen Codes wird jedoch nicht gestoppt. Das Programm zeigt die Lotterienummern, Prognosen und Ergebnisse eines Baseballspiels an. Das Programm druckt die Nachrichten, nachdem die Sammlung von gatherNewsReports()
. Wenn gatherNewsReports()
einige Zeit benötigt, um seine Arbeit abzuschließen, passiert nichts Schlimmes: Der Benutzer kann andere Dinge lesen, bevor der tägliche News Digest gedruckt wird.
Achten Sie auf Rückgabetypen. Der Rückgabetyp der Funktion gatherNewsReports()
ist Future<String>
gatherNewsReports()
bedeutet, dass eine future
, die mit einem Zeichenfolgenwert endet. Die Funktion printDailyNewsDigest()
, die keinen Wert printDailyNewsDigest()
, hat den Rückgabetyp Future<void>
.
Das folgende Diagramm zeigt die Schritte zur Codeausführung.

- Die Anwendung wird gestartet.
- Die
main()
Funktion printDailyNewsDigest()
asynchrone Funktion printDailyNewsDigest()
und wird synchron ausgeführt. printDailyNewsDigest()
verwendet await
, um die Funktion gatherNewsReports()
aufzurufen, die gatherNewsReports()
wird.gatherNewsReports()
gibt eine unvollendete future
(eine Instanz von Future<String>
).- Da
printDailyNewsDigest()
eine asynchrone Funktion ist und einen Wert erwartet, wird die Ausführung printDailyNewsDigest()
und die unvollständige future
(in diesem Fall Future<void>
) an die aufrufende Funktion main ()
. - Die restlichen Ausgabefunktionen werden ausgeführt. Da sie synchron sind, wird jede Funktion vollständig ausgeführt, bevor zur nächsten übergegangen wird. Beispielsweise werden alle Lottozahlen vor der Wettervorhersage angezeigt.
- Nach Abschluss von
main()
asynchrone Funktionen die Ausführung fortsetzen. Zuerst erhalten wir die future
mit Neuigkeiten über die Fertigstellung von gatherNewsReports()
. Anschließend setzt printDailyNewsDigest()
die Ausführung fort und zeigt die Nachrichten an. - Am Ende der Ausführung von
printDailyNewsDigest()
ist die ursprünglich empfangene future
und die Anwendung wird beendet.
Beachten Sie, dass die asynchrone Funktion sofort (synchron) gestartet wird. Die Funktion unterbricht die Ausführung und gibt eine unvollendete future
wenn das erste Auftreten eines der folgenden Ereignisse eintritt:
- Der erste
await
Ausdruck (nachdem die Funktion aus diesem Ausdruck die unvollständige future
). - Jede
return
in einer Funktion. - Das Ende des Funktionskörpers.
Fehlerbehandlung
Höchstwahrscheinlich möchten Sie einen Fehler bei der Ausführung der Funktion "abfangen", die die future
zurückgibt. In asynchronen Funktionen können Sie Fehler mit try-catch
:
Future<void> printDailyNewsDigest() async { try { var newsDigest = await gatherNewsReports(); print(newsDigest); } catch (e) {
Ein try-catch
mit asynchronem Code verhält sich genauso wie mit synchronem Code: Wenn der Code im try
Block eine Ausnahme try
, wird der Code in catch
ausgeführt.
Sequentielle Ausführung
Sie können mehrere await
, um sicherzustellen, dass jede Anweisung abgeschlossen ist, bevor Sie Folgendes ausführen:
expensiveB()
Funktion expensiveB()
wird erst ausgeführt, wenn die Funktion expensiveA()
abgeschlossen ist, und so weiter.
Zukünftige API
Bevor async
und await
in Dart 1.9 hinzugefügt wurden, mussten Sie die Future
API verwenden. Sie können die Verwendung der Future
API immer noch in altem Code und in Code sehen, der mehr Funktionen benötigt als async–await
zu bieten hat.
Verwenden Sie die then()
-Methode, um asynchronen Code mithilfe der Future
API zu schreiben und den Rückruf zu registrieren. Dieser Rückruf funktioniert, wenn die future
abgeschlossen ist.
Der folgende Code simuliert das Lesen von Nachrichten aus einer Datei mithilfe der Future
API. Öffnen Sie DartPad mit der Anwendung , starten Sie und klicken Sie auf KONSOLE, um das Ergebnis anzuzeigen.
Beachten Sie, dass wir zuerst printDailyNewsDigest()
aufrufen, die Nachrichten jedoch zuletzt gedruckt werden, selbst wenn die Datei nur eine Zeile enthält. Dies liegt daran, dass der Code, der die Datei liest und druckt, asynchron ausgeführt wird.
Diese Anwendung läuft wie folgt ab:
- Die Anwendung wird gestartet.
- Die Hauptfunktion ruft
printDailyNewsDigest()
, das das Ergebnis nicht sofort gatherNewsReports()
, sondern zuerst gatherNewsReports()
. gatherNewsReports()
beginnt mit dem Lesen von Nachrichten und gibt die future
.printDailyNewsDigest()
verwendet then()
, um einen Rückruf zu registrieren, der den am Ende der future
erhaltenen Wert als Parameter verwendet. Der Aufruf then()
gibt eine neue future
, die mit dem Wert endet, den der Rückruf von then()
zurückgibt.- Die restlichen Ausgabefunktionen werden ausgeführt. Da sie synchron sind, wird jede Funktion vollständig ausgeführt, bevor zur nächsten übergegangen wird. Beispielsweise werden alle Lottozahlen vor der Wettervorhersage angezeigt.
- Wenn alle Nachrichten empfangen wurden, endet die von der Funktion
gatherNewsReports()
future
mit einer Zeichenfolge, die die gesammelten Nachrichten enthält. - Der in
then()
in printDailyNewsDigest()
angegebene Code wird ausgeführt, printDailyNewsDigest()
Nachrichten zu printDailyNewsDigest()
. - Die Anwendung wird heruntergefahren.
Hinweis: In der Funktion printDailyNewsDigest()
entspricht der Code future.then(print)
dem folgenden: future.then((newsDigest) => print(newsDigest))
.
Außerdem kann der Code in then()
geschweifte Klammern verwenden:
Future<void> printDailyNewsDigest() { final future = gatherNewsReports(); return future.then((newsDigest) { print(newsDigest);
Sie müssen das Rückrufargument in then()
angeben, auch wenn future
vom Typ Future<void>
. Konventionell wird ein nicht verwendetes Argument durch _
(Unterstrich) definiert.
final future = printDailyNewsDigest(); return future.then((_) {
Fehlerbehandlung
Mit der Future
API können Sie den Fehler mit catchError()
:
Future<void> printDailyNewsDigest() => gatherNewsReports().then(print).catchError(handleError);
Wenn die Nachrichten nicht lesbar sind, wird der obige Code wie folgt ausgeführt:
future
von gatherNewsReports()
future
schlägt fehl.future
von then()
future
schlägt fehl, print()
nicht aufgerufen.- Der
catchError()
in catchError()
( handleError()
) fängt den Fehler ab, die von catchError()
future
wird catchError()
abgeschlossen und der Fehler breitet sich nicht weiter aus.
Die then()
-Kette - catchError()
ist ein gängiges Muster bei Verwendung der Future
API. Betrachten Sie dieses Paar als das Äquivalent eines try-catch
in der Future
API.
Wie then () gibt catchError () eine neue future
, die mit dem Rückgabewert des Rückrufs endet. Um in das Thema einzutauchen , lesen Sie Futures und Fehlerbehandlung .
Aufruf mehrerer Funktionen, die die future
Betrachten wir drei Funktionen: expensiveA()
, expensiveB()
, expensiveC()
, die die future
. Sie können sie nacheinander aufrufen (eine Funktion startet nach Abschluss der vorherigen) oder Sie können sie alle gleichzeitig ausführen und etwas tun, sobald alle Werte zurückgegeben werden. Die Benutzeroberfläche von Future ist flexibel genug, um beide Anwendungsfälle zu implementieren.
Eine Funktionskette ruft mit then()
Wenn die Funktionen, die die future
zurückgeben, in der richtigen Reihenfolge ausgeführt werden müssen, verwenden Sie die Kette von then()
:
expensiveA() .then((aValue) => expensiveB()) .then((bValue) => expensiveC()) .then((cValue) => doSomethingWith(cValue));
Das Anhängen von Rückrufen funktioniert ebenfalls, ist jedoch schwieriger zu lesen. ( Hinweis http://callbackhell.com/ )
Warten auf den Abschluss mehrerer futures
mit Future.wait()
Wenn die Ausführungsreihenfolge der Funktionen nicht wichtig ist, können Sie Future.wait()
. Wenn Sie die futures
Liste für Parameter für die Future.wait () -Funktion angeben, wird sofort die future
. Diese future
endet erst, wenn alle angegebenen futures
. Diese future
endet mit einer Liste der Ergebnisse aller angegebenen futures
.
Future.wait([expensiveA(), expensiveB(), expensiveC()]) .then((List responses) => chooseBestResponse(responses, moreInfo)) .catchError(handleError);
Wenn ein Aufruf einer der Funktionen fehlschlägt, schlägt auch die von Future.wait()
future
fehl. Verwenden Sie catchError()
, um diesen Fehler catchError()
.
Was noch zu lesen?
Dart 2. Asynchrone Programmierung: Datenströme