Bibliothek dart:async

dart:async stellt die zentralen Fähigkeiten für die asynchrone Programmierung in Dart bereit: Future, Stream.

  1. Für die Nutzung von Future / Stream muss dart:async nicht zwingend manuell importiert werden, da dart:core diese Typen bereits exportiert;
  2. Sollen Typen wie StreamController, StreamSubscription, ParallelWaitError verwendet werden, ist ein expliziter Import erforderlich:
import 'dart:async';Code-Sprache: JavaScript (javascript)

Zwei Kernkonzepte:

  • Future: Steht für ein einmaliges asynchrones Ergebnis (es wird zu einem späteren Zeitpunkt genau ein Wert oder ein Fehler erzeugt)
  • Stream: Steht für eine fortlaufende Reihe asynchroner Daten (es entstehen nacheinander mehrere Werte, vergleichbar mit einem Ereignisstrom)

Future

1. async / await

Die bevorzugte Schreibweise mit besserer Lesbarkeit als verkettete .then()-Aufrufe

  • Funktionen mit dem Schlüsselwort async geben immer ein Future zurück;
  • await wartet auf den Abschluss eines Futures und hält die Ausführung des aktuellen Codes bis zur Ergebnisrückgabe an;
  • Mit try-catch lassen sich asynchrone Ausnahmen abfangen.
// async 函数返回 Future
Future<String> findEntryPoint() async => "main.dart";
Future<int> runExecutable(String entry, List args) async => 0;
Future<void> flushThenExit(int code) async {}

// await 写法(推荐)
Future<void> runUsingAsyncAwait() async {
  try {
    var entryPoint = await findEntryPoint();
    var exitCode = await runExecutable(entryPoint, []);
    await flushThenExit(exitCode);
  } catch (e) {
    // 统一捕获异常
    print("出错:$e");
  }
}

2. .then() / .catchError() traditionelle verkettete Syntax

  • .then(): Führt einen Callback aus, wenn das Future erfolgreich abgeschlossen wird. Es gibt ein neues Future zurück, sodass die Kette weitergeführt werden kann
  • .catchError(): Fängt Fehler ab; muss an den Rückgabewert von then angehängt werden, sonst werden Fehler innerhalb des then-Blocks nicht erkannt
void runUsingFuture() {
  findEntryPoint()
      .then((entryPoint) {
        return runExecutable(entryPoint, []);
      })
      .then(flushThenExit)
      .catchError((e) {
        print("异常:$e");
      });
}

3. Auf mehrere parallele Futures warten

① Future.wait()

Wartet bis alle Aufgaben beendet sind; schlägt der gesamte Vorgang fehl, sobald nur ein Future einen Fehler liefert

Future<void> task1() async {}
Future<void> task2() async {}
Future<void> task3() async {}

void testWait() async {
  await Future.wait([
    task1(),
    task2(),
    task3(),
  ]);
  print("全部任务执行完成");
}
② List.wait / Record.wait (neues Feature, differenzierte Fehlerbehandlung bei parallelen Vorgängen)

Löst eine ParallelWaitError aus. Dadurch lässt sich feststellen, welche Vorgänge erfolgreich und welche fehlgeschlagen sind – ohne gegenseitige Unterbrechung.

Future<int> delete() async => 1;
Future<String> copy() async => "ok";
Future<bool> errorTask() async {
  throw Exception("任务失败");
}

void main() async {
  try {
    // 列表形式
    var results = await [delete(), copy(), errorTask()].wait;
  } on ParallelWaitError<List<Object?>, List<AsyncError?>> catch (e) {
    print(e.values);   // 成功有值,失败为null
    print(e.errors);   // 成功为null,失败存放错误信息
  }

  // Record元组形式:支持不同类型Future
  try {
    final (resInt, resStr, resBool) = await (delete(), copy(), errorTask()).wait;
  } on ParallelWaitError<(int?, String?, bool?), (AsyncError?, AsyncError?, AsyncError?)> catch(e) {

  }
}

Stream

Ein Stream steht für eine fortlaufende Folge asynchroner Daten (zeilenweises Einlesen von Dateien, Schaltflächenklick-Ereignisse, Netzwerkdatenströme).

Zwei Verwendungsarten

1:await for asynchrone for-Schleife (übersichtlich, ähnlich der synchronen Iteration)

Geeignet für endliche Datenströme; nicht für unendliche Ereignisströme (z. B. Oberflächenklicks), da nachfolgender Code blockiert wird.

import 'dart:io';

Future<void> readFileByAwaitFor() async {
  var file = File("config.txt");
  Stream<List<int>> input = file.openRead();
  var lines = input
      .transform(utf8.decoder)
      .transform(const LineSplitter());

  try {
    await for (final line in lines) {
      print("读取一行:$line");
    }
    print("文件读取完毕");
  } catch (e) {
    print("读取异常 $e");
  }
}
2 .listen() Abonnement zum Lauschen (für unendliche Ereignisströme geeignet)
inputStream.listen(
  (data) {
    // 收到数据回调
  },
  onError: (err) {
    // 发生错误
  },
  onDone: () {
    // 流正常关闭完成
  },
);

listen gibt ein StreamSubscription zurück. Darauf kann .cancel() aufgerufen werden, um das Abonnement zu beenden.

Häufig genutzte Stream-Methoden

  • Filtern:where()
  • Begrenzen:take()takeWhile()
  • Elemente überspringen:skip()skipWhile()
  • Einzelnes Element abrufen:firstlastsinglefirstWhere()

Datenumwandlung bei Streams mit transform()

Ändert den Datentyp des Stroms mithilfe eines Transformators (typischer Anwendungsfall: Binärstrom zu Zeichenkette umwandeln)

var lines = inputStream
    .transform(utf8.decoder)       // List<int> → String
    .transform(const LineSplitter()); // String → 逐行字符串

Auswahlhilfe: listen() vs await for

  1. Dateilesen, endliche Sequenzdaten → await for
  2. UI-Ereignisse, dauernde Nachrichtenübermittlung und weitere unendliche Ströme.listen()
  3. Sobald await for startet, wartet es bis zum Ende des Stroms. Bei unendlichen Ereignisströmen bleibt das Programm hängen

Bibliothek dart:async

Previous:

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert