Working with asynchronous code and Futures is common in Dart and Flutter. It's important to handle potential errors properly.
A Basic Failing Function
Let's look at a simple Future function that throws an error:
Future<void> functionInError() async {
throw "I am failing";
}
How NOT to Handle Errors
Avoid handling errors like this:
functionInError()
.catchError((err) => print("error catched"))
.then(
(value) => print("success"),
onError: (err) => print("I failed"),
);
// Result: ------\n// error catched \n// success
Why? Because .catchError stops the error from going down the chain. The .then's onError won't be called, and you might get unexpected "success" results.
The Correct Way with .catchError (If You Need to Rethrow)
If you use .catchError but want the error to continue, rethrow it:
functionInError()
.catchError((err) {
print("error catched");
throw err;
})
.then(
(value) => print("success"),
onError: (err) => print("I failed"),
);
// Result: ------\n// error catched \n// I failed
This catches the error, does something, and then throws it again so the next onError can handle it.
Prefer Handling Errors in onError
Generally, it's better to handle errors directly in the onError callback within .then or use catchError only when you intend to stop the error propagation or transform it.
Chaining Futures and Errors
When you chain Futures, errors are passed down the chain to the latest available onError handler:
void main() {
functionInError()
.then((res) => workingFuture())
.then((res) => print("ended"),
onError: (err) => print("error catched") // This catches the error from functionInError()
);
}
Future<void> workingFuture() async {
print("working future");
}
Future<void> functionInError() async {
throw "I am failing";
}
// Result:\n// error catched
Using try...catch with await
A clean way to handle errors with asynchronous code is using try...catch with the await keyword. This makes async code look and behave more like sync code.
void main() async {
try {
await functionInError();
} catch (err) {
print('I failed');
}
}
// Result:\n// I failed
This approach is often easier to read and manage.
Choose the method that best fits your code structure and how you want errors to flow through your asynchronous operations.