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.