Why Have Error & Exception Handling?
Most software systems are complicated and written by a team of people.
Complexity arises from multiple sources:
- The business domain.
- The act of writing software.
- From multiple people working together, each one having different viewpoints.
- etc
The complexity can result in misunderstandings, errors & exceptions.
This is not the end of the world if the code has good error handling.
- If you don't handle your errors & exceptions, your software may act unpredictably, and users may suffer a catastrophic error without knowing it or being able to detect when it happened.
- If you do handle your errors & exceptions, the user may able to continue using the program even with the error / exception and the developers can find the problems over time and improve the software.
Good error & exception handling should not blind the end user with technical jargon, but it should also provide enough information for the developers to trace down the problem.
Dart can throw Errors & Exceptions when problems occur running a Dart program. When an Error or an Exception occurs, normal flow of the program is disrupted, and the program terminates abnormally.
Errors and Exceptions
Errors
Errors are serious issues that cannot be caught and ‘dealt with’. Non-recoverable.
Examples
- RangeError – programmatic bug where user is attempting to use an invalid index to retrieve a List element.
- OutOfMemoryError
Exceptions
Exceptions are less-serious issues that can be caught and ‘dealt with’.
Recoverable.
Examples
- FormatException – could not parse a String.
Handling Errors
Trying to handle non-recoverable errors is impossible. How can you catch and just handle an out of memory error?
The best thing to do is to log what happened and where so that the developers can deal with them. The approach to this is to add a handler to the top level of your application, for example Sentry or Catcher.
Further Reading
https://medium.com/flutter-community/handling-flutter-errors-with-catcher-efce74397862
Handling Exceptions
Try to handle these to prevent the application from terminating abruptly. If you want your code to handle exceptions then you need to place it in a ‘try..catch..finally’ block. The finally part is optional.
Finally
Dart also provides a finally block that will always be executed no matter if any exception is thrown or not.
Example Code
void main() {
try {
// do something here
} catch (e) {
// print exception
print(e);
} finally {
// always executed
print('I will always be executed!');
}
}
Catch Exception
The first argument to the catch is the Exception.
Example Code
This code catches the Exception and prints it out.
void main() {
print('start');
try {
int.parse("mark");
} catch (ex) {
print(ex);
}
print('finish');
}
Example Code Output
start
FormatException: mark
finish
Catch Exception and Stack Trace
The second argument to the catch is the StackTrace.
Strack Trace
A Stack Trace is a list of the method calls that the application was in the middle of when an Exception was thrown. The most useful information is normally shown at the top of StackTraces, so you should always look at them from the ‘top down’. Sometimes this takes a lot of scrolling up!
Example Code
This code catches the Exception and StackTrace.
void main() {
print('start');
try {
int.parse("mark");
} catch (ex, stacktrace) {
print(stacktrace);
}
print('finish');
}
Example Code Output
start
FormatException: mark
FormatException: mark
at Object.wrapException (<anonymous>:370:17)
at Object.int_parse (<anonymous>:1555:15)
at main (<anonymous>:1702:11)
at dartMainRunner (<anonymous>:9:5)
at <anonymous>:2206:7
at <anonymous>:2192:7
at dartProgram (<anonymous>:2203:5)
at <anonymous>:2210:3
at replaceJavaScript (https://dartpad.dartlang.org/scripts/frame.html:39:17)
at https://dartpad.dartlang.org/scripts/frame.html:69:7
finish
Catch Specific Exceptions
If you know you want to catch a specific Exception then you can use an ‘on’ instead of a ‘catch’. Consider leaving a ‘catch’ at the bottom to catch other Exceptions.
You can optionally add the ‘catch(e)’ or catch(e, s)’ after if you want the Exception and StackTrace data as arguments.
Example Code
void main() {
print('start');
try {
int.parse("mark");
} on FormatException{
print('invalid string');
} catch (ex,stacktrace) {
print(stacktrace);
}
print('finish');
}
Example Code Output
start
invalid string
finish
Throw Exception
To throw an Exception simply use the ‘throws’ keyword and instantiate the Exception.
Example Code
throw new TooOldForServiceException();
Rethrow Exception
Once you have caught an Exception, you have the option of rethrowing it so that it bubbles up to the next level. So, you could catch an Exception, log it then rethrow it so it is dealt with at a higher level.
Example Code
void misbehave() {
try {
dynamic foo = true;
print(foo++); // Runtime error
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
rethrow; // Allow callers to see the exception.
}
}
void main() {
try {
misbehave();
} catch (e) {
print('main() finished handling ${e.runtimeType}.');
}
}
Output
misbehave() partially handled JsNoSuchMethodError.
main() finished handling JsNoSuchMethodError.
Create Custom Exceptions
It is very simple to create your own custom Exception.
Simply implement the Exception interface.
Example Code
class TooOldForServiceException implements Exception {
Cadet _cadet;
TooOldForServiceException(this._cadet);
toString(){
return "${_cadet.name} is too old to be in military service.";
}
}
class Cadet {
String _name;
int _age;
Cadet(this._name, this._age);
get age{
return _age;
}
get name{
return _name;
}
}
void main() {
print('start');
List<Cadet> cadetList = [
Cadet("Tom", 21),
Cadet("Dick", 37),
Cadet("Harry", 51),
Cadet("Mark", 52),
];
List<Cadet> validCadetList = [];
for (Cadet cadet in cadetList){
try {
validateCadet(cadet);
validCadetList.add(cadet);
} on TooOldForServiceException catch(ex) {
print(ex);
} // .. other validation exceptions ...
}
print('finish: ${validCadetList.length} of ${cadetList.length} cadets are valid.');
}
void validateCadet(Cadet cadet){
if (cadet.age > 50){
throw new TooOldForServiceException(cadet);
}
// .. other validations ...
}
Example Code Output
start
Harry is too old to be in military service.
Mark is too old to be in military service.
finish: 2 of 4 cadets are valid.