İçeriğe geç / Skip to content / Zum Inhalt
Ahmet Balaman LogoAhmet Balaman

Fehlerbehandlung in Dart: try-catch und asynchrone Abläufe

Ahmet Balaman
DartTry CatchAsyncAwaitFutureError Handling

Fehlerbehandlung und asynchrone Programmierung

Heute habe ich zwei wichtige Themen gelernt: Fehlerbehandlung (try-catch) und asynchrone Abläufe (async/await). Beide sind in echten Anwendungen sehr wichtig.

Live-Demo: try-catch und async/await

Lernen Sie Fehlerbehandlung und asynchrone Programmierung interaktiv:

try-catch – Fehler abfangen

Unsere Programme laufen nicht immer fehlerfrei. Nutzer geben etwas Falsches ein, die Internetverbindung bricht ab, Dateien werden nicht gefunden. Genau dafür gibt es try-catch.

Das erste Problem, auf das ich stieß

Ich frage den Nutzer nach einer Zahl, er gibt aber einen Buchstaben ein. Das Programm stürzt ab:

import 'dart:io';

void main() {
  print("Enter a number:");
  String input = stdin.readLineSync()!;
  int number = int.parse(input); // ERROR if letters are entered!
  print("Your number: $number");
}

Schreibt der Nutzer „abc“, fliegt das Programm auseinander. Die Lösung? try-catch!

Mit try-catch absichern

import 'dart:io';

void main() {
  print("Enter a number:");
  
  try {
    String input = stdin.readLineSync()!;
    int number = int.parse(input);
    print("Your number: $number");
  } catch (e) {
    print("Error! Please enter a valid number.");
    print("Error detail: $e");
  }
}

Jetzt stürzt das Programm nicht mehr ab, egal was der Nutzer schreibt. Tritt ein Fehler auf, läuft der catch-Block.

Ein Programm, das so lange nachfragt, bis die Eingabe stimmt

Das habe ich noch einen Schritt weitergeführt: so lange fragen, bis der Nutzer eine korrekte Zahl eingibt:

import 'dart:io';

void main() {
  int number;
  
  while (true) {
    try {
      print("Enter a number:");
      number = int.parse(stdin.readLineSync()!);
      break; // Exit loop if successful
    } catch (e) {
      print("Please enter a valid number!");
    }
  }
  
  print("Congratulations! Your number: $number");
}

Dieser Code läuft weiter, bis der Nutzer die richtige Zahl eingibt. Sehr praktisch!

Der finally-Block

Manchmal soll Code unabhängig davon laufen, ob ein Fehler auftrat oder nicht:

try {
  // Risky operation
  int result = 10 ~/ 0; // Division by zero error
} catch (e) {
  print("Error occurred: $e");
} finally {
  print("This always runs");
}

Der finally-Block läuft in jedem Fall. Er wird meist genutzt, um Dateien zu schließen oder Verbindungen zu trennen.

Asynchrone Abläufe – async und await

Die asynchrone Programmierung hat mich anfangs stark gefordert. So habe ich sie verstanden: Manche Vorgänge dauern (Daten aus dem Internet holen, Dateien lesen usw.), und das Programm soll während des Wartens nicht einfrieren.

Synchron im Vergleich zu asynchron

Synchron (normal):

Operation 1 start → Operation 1 finish → Operation 2 start → Operation 2 finish

Asynchron:

Operation 1 start → Operation 2 start → Operation 1 finish → Operation 2 finish

Sie können also andere Dinge erledigen, während Sie auf etwas warten.

Future, async, await – ein Dreiklang

Diese drei Begriffe treten immer gemeinsam auf:

  • Future: Ein Vorgang, der in der Zukunft abgeschlossen wird
  • async: Kennzeichnet die Funktion als asynchron
  • await: Wartet, bis der Vorgang abgeschlossen ist

Mein erstes async-Beispiel

Future<void> main() async {
  print("Waiting for data...");
  
  String data = await getDataFromDatabase();
  
  print("Data received: $data");
}

Future<String> getDataFromDatabase() async {
  // Wait 3 seconds (simulating database)
  await Future.delayed(Duration(seconds: 3));
  return "User data";
}

Dieser Code arbeitet so:

  1. „Warte auf Daten …“ ausgeben
  2. Die Funktion getDataFromDatabase() aufrufen und warten (3 Sekunden)
  3. Wenn die Daten eintreffen, „Daten erhalten: …“ ausgeben

Was passiert ohne await?

Future<void> main() async {
  print("Waiting for data...");
  
  String data = getDataFromDatabase(); // ERROR!
  
  print("Data received: $data");
}

Das funktioniert nicht, weil getDataFromDatabase() nicht sofort einen Wert liefert, sondern ein Future. Ohne await lässt sich ein Future nicht in einen String umwandeln.

Ein realistischeres Beispiel

Future<void> main() async {
  print("1. Waiting for data...");
  var data = await getDataFromDatabase();
  print("2. Getting data: $data");
}

Future<String> getDataFromDatabase() async {
  // Show loading status every second
  for (var i = 1; i <= 5; i++) {
    await Future.delayed(
      Duration(seconds: 1),
      () => print("Loading... %${i * 20}")
    );
  }
  
  return "Database cluster";
}

Ausgabe:

1. Waiting for data...
Loading... %20
Loading... %40
Loading... %60
Loading... %80
Loading... %100
2. Getting data: Database cluster

try-catch zusammen mit async verwenden

Auch bei asynchronen Abläufen können Fehler auftreten (Verbindungsabbruch, Zeitüberschreitung usw.):

Future<void> main() async {
  try {
    print("Fetching data from API...");
    String data = await fetchDataFromAPI();
    print("Data: $data");
  } catch (e) {
    print("Error occurred: $e");
    print("Check your internet connection");
  }
}

Future<String> fetchDataFromAPI() async {
  await Future.delayed(Duration(seconds: 2));
  
  // Error simulation
  throw Exception("Could not connect to server!");
  
  // return "API Data"; // This would normally work
}

Verwendung in Flutter

In Flutter nutzen wir async ständig. Denn:

  • Wir holen Daten von APIs
  • Wir führen Datenbankoperationen aus
  • Wir lesen Dateien
  • Wir warten auf Eingaben der Nutzer

Beispiel für einen API-Aufruf in Flutter

class DataPage extends StatefulWidget {
  @override
  _DataPageState createState() => _DataPageState();
}

class _DataPageState extends State<DataPage> {
  String data = "Loading...";
  
  @override
  void initState() {
    super.initState();
    loadData();
  }
  
  Future<void> loadData() async {
    try {
      // Make API call (example)
      await Future.delayed(Duration(seconds: 2));
      
      setState(() {
        data = "Data loaded successfully!";
      });
    } catch (e) {
      setState(() {
        data = "Error: $e";
      });
    }
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text(data),
      ),
    );
  }
}

Das Widget FutureBuilder

In Flutter gibt es ein eigenes Widget, um Futures anzuzeigen:

FutureBuilder<String>(
  future: getDataFromDatabase(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return CircularProgressIndicator(); // Show loading
    }
    
    if (snapshot.hasError) {
      return Text("Error: ${snapshot.error}"); // Show error
    }
    
    return Text("Data: ${snapshot.data}"); // Show data
  },
)

Dieses Widget verwaltet die Zustände Laden, Fehler und Erfolg automatisch. Sehr praktisch!

Worauf Sie bei async/await achten sollten

Ein paar wichtige Regeln, die ich gelernt habe:

  1. await wird nur in async-Funktionen verwendet
// WRONG
void function() {
  await something(); // ERROR!
}

// CORRECT
Future<void> function() async {
  await something(); // OK
}
  1. Eine async-Funktion muss ein Future zurückgeben
Future<String> getData() async {
  return "Data";
}
  1. Sie können mehrere await verwenden
Future<void> getAllData() async {
  String data1 = await getDataFromAPI1();
  String data2 = await getDataFromAPI2();
  String data3 = await getDataFromAPI3();
  
  print("$data1, $data2, $data3");
}
  1. Für parallele Ausführung nutzen Sie Future.wait
Future<void> parallelData() async {
  // All three start at the same time
  var results = await Future.wait([
    getDataFromAPI1(),
    getDataFromAPI2(),
    getDataFromAPI3(),
  ]);
  
  print(results); // [data1, data2, data3]
}

Was habe ich heute gelernt?

  • try-catch fängt Fehler sicher ab
  • Der finally-Block läuft in jedem Fall
  • async/await verwaltet asynchrone Abläufe
  • Future steht für Vorgänge, die in der Zukunft abgeschlossen werden
  • FutureBuilder ist in Flutter sehr praktisch

Morgen sehe ich mir den Oberflächenaufbau von Flutter an (Widget-Baum, Row, Column, Stack). Nachdem die Codeseite sitzt, ist jetzt die Oberfläche dran!


Noch Fragen?

Viel Spaß beim Programmieren! 🚀

Kommentare