Fehlerbehandlung in Dart: try-catch und asynchrone Abläufe
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 finishAsynchron:
Operation 1 start → Operation 2 start → Operation 1 finish → Operation 2 finishSie 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:
- „Warte auf Daten …“ ausgeben
- Die Funktion
getDataFromDatabase()aufrufen und warten (3 Sekunden) - 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 clustertry-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:
- await wird nur in async-Funktionen verwendet
// WRONG
void function() {
await something(); // ERROR!
}
// CORRECT
Future<void> function() async {
await something(); // OK
}- Eine async-Funktion muss ein Future zurückgeben
Future<String> getData() async {
return "Data";
}- 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");
}- 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! 🚀