はじめに
Dart では例外をどのようにハンドリングしたらいいの? return Future.error(~)
ってなに? という疑問が湧いたので色々と試してみました。
分かったこと
- Dart では
Exception
だけでなく、Error
や任意のオブジェクト (String
等) をスローできる - 任意のオブジェクト (例えば
String
) は} on String catch (e) {
として明示的にキャッチできる -
Error
は} on Error catch (e) {
としてもキャッチできず、} catch (e) {
に流れる - 非同期メソッド内で
return Future.error([任意のオブジェクト]);
とした場合は呼び元に任意のオブジェクトがスローされる -
try
句で「await
なし」でコールした非同期メソッドからスローされた例外はUncaught Error
として扱われる -
then
句で「await
なし」でコールした非同期メソッドからスローされた例外はcatchError
句で処理できる
検証
例外をスローする非同期メソッド
// [A] Exception をスロー
Future _execA() async {
throw Exception('Hello Exception !!');
}
// [B] Error をスロー
Future _execB() async {
throw Error;
}
// [C] String をスロー
Future _execC() async {
throw 'Hello String !!';
}
// [D] Future.error(String) を返す
Future _execD() async {
return Future.error('Hello Future.error(String) !!');
}
// [E] Future.error(Exception) を返す
Future _execE() async {
return Future.error(Exception('Hello Future.error(Exception) !!'));
}
01-05. try 内での例外スロー [await あり]
catch
では、Exception
、Error
、String
を明示的にキャッチします。
// 01: Exception をスロー
try {
await _execA();
} on Exception catch (e) {
print('01a: $e'); // ★ ここに入る
} on Error catch (e) {
print('01b: $e');
} on String catch (e) {
print('01c: $e');
} catch (e) {
print('01d: $e');
}
// 02: Error をスロー
try {
await _execB();
} on Exception catch (e) {
print('02a: $e');
} on Error catch (e) {
print('02b: $e'); // ここには入らない !?
} on String catch (e) {
print('02c: $e');
} catch (e) {
print('02d: $e'); // ★ ここに入る
}
// 03: String をスロー
try {
await _execC();
} on Exception catch (e) {
print('03a: $e');
} on Error catch (e) {
print('03b: $e');
} on String catch (e) {
print('03c: $e'); // ★ ここに入る
} catch (e) {
print('03d: $e');
}
// 04: Future.error(String) を返す
try {
await _execD();
} on Exception catch (e) {
print('04a: $e');
} on Error catch (e) {
print('04b: $e');
} on String catch (e) {
print('04c: $e'); // ★ ここに入る
} catch (e) {
print('04d: $e');
}
// 05: Future.error(Exception) を返す
try {
await _execE();
} on Exception catch (e) {
print('05a: $e'); // ★ ここに入る
} on Error catch (e) {
print('05b: $e');
} on String catch (e) {
print('05c: $e');
} catch (e) {
print('05d: $e');
}
この結果はこんな感じ、、
01a: Exception: Hello Exception !!
02d: Error
03c: Hello String !!
04c: Hello Future.error(String) !!
05a: Exception: Hello Future.error(Exception) !!
Error
がスローされても } on Error catch (e) {
には入らないんですね !?
} catch (e) {
に流れてきちゃいました。これは気を付けないと。
return Future.error( ... );
は引数で渡したオブジェクトが呼び元にスローされるんですね。
06-10. try 内での例外スロー [await なし]
次は、try
句で非同期メソッドを「await
なし」で呼び出します。
// 6: Exception をスロー [await なし]
try {
_execA();
} on Exception catch (e) {
print('06a: $e');
} on Error catch (e) {
print('06b: $e');
} on String catch (e) {
print('06c: $e');
} catch (e) {
print('06d: $e');
}
// ★ Uncaught Error
// 7: Error をスロー [await なし]
try {
_execB();
} on Exception catch (e) {
print('07a: $e');
} on Error catch (e) {
print('07b: $e');
} on String catch (e) {
print('07c: $e');
} catch (e) {
print('07d: $e');
}
// ★ Uncaught Error
// 8: String をスロー [await なし]
try {
_execC();
} on Exception catch (e) {
print('08a: $e');
} on Error catch (e) {
print('08b: $e');
} on String catch (e) {
print('08c: $e');
} catch (e) {
print('08d: $e');
}
// ★ Uncaught Error
// 9: Future.error(String) を返す [await なし]
try {
_execD();
} on Exception catch (e) {
print('09a: $e');
} on Error catch (e) {
print('09b: $e');
} on String catch (e) {
print('09c: $e');
} catch (e) {
print('09d: $e');
}
// ★ Uncaught Error
// 10: Future.error(Exception) を返す [await なし]
try {
_execE();
} on Exception catch (e) {
print('10a: $e');
} on Error catch (e) {
print('10b: $e');
} on String catch (e) {
print('10c: $e');
} catch (e) {
print('10d: $e');
}
// ★ Uncaught Error
この結果はこんな感じ、、
Uncaught Error: Exception: Hello Exception !!
Uncaught Error: Error
Uncaught Error: Hello String !!
Uncaught Error: Hello Future.error(String) !!
Uncaught Error: Exception: Hello Future.error(Exception) !!
全て Uncaught Error
として扱われるんですね。
11-15. then - catchError
次は非同期メソッドの戻り値の Future
を then - catchError
で処理します。
// 11: Exception をスロー
_execA()
.then((n) => print('11:'),
onError: (e) => print('11e: $e')) // ★ ここに入る
.catchError((e) => print('11f: $e'));
// 12: Error をスロー
_execB()
.then((n) => print('12:'),
onError: (e) => print('12e: $e')) // ★ ここに入る
.catchError((e) => print('12f: $e'));
// 13: String をスロー
_execC()
.then((n) => print('13:'),
onError: (e) => print('13e: $e')) // ★ ここに入る
.catchError((e) => print('13f: $e'));
// 14: Future.error(String) を返す
_execD()
.then((n) => print('14:'),
onError: (e) => print('14e: $e')) // ★ ここに入る
.catchError((e) => print('14f: $e'));
// 15: Future.error(Exception) を返す
_execE()
.then((n) => print('15:'),
onError: (e) => print('15e: $e')) // ★ ここに入る
.catchError((e) => print('15f: $e'));
この結果はこんな感じ、、
11e: Exception: Hello Exception !!
12e: Error
13e: Hello String !!
14e: Hello Future.error(String) !!
15e: Exception: Hello Future.error(Exception) !!
非同期メソッドがすべて例外をスローするので、then
には入らず、すべて onError
に入ります。
16-20. then 内での例外スロー [await あり]
次は then
句内で非同期メソッドを呼びます。
// 16: Exception をスロー
Future.delayed(Duration.zero)
.then((n) async => await _execA(),
onError: (e) => print('16e: $e'))
.catchError((e) => print('16f: $e')); // ★ ここに入る
// 17: Error をスロー
Future.delayed(Duration.zero)
.then((n) async => await _execB(),
onError: (e) => print('17e: $e'))
.catchError((e) => print('17f: $e')); // ★ ここに入る
// 18: String をスロー
Future.delayed(Duration.zero)
.then((n) async => await _execC(),
onError: (e) => print('18e: $e'))
.catchError((e) => print('18f: $e')); // ★ ここに入る
// 19: Future.error(String) を返す
Future.delayed(Duration.zero)
.then((n) async => await _execD(),
onError: (e) => print('19e: $e'))
.catchError((e) => print('19f: $e')); // ★ ここに入る
// 20: Future.error(Exception) を返す
Future.delayed(Duration.zero)
.then((n) async => await _execE(),
onError: (e) => print('20e: $e'))
.catchError((e) => print('20f: $e')); // ★ ここに入る
この結果はこんな感じ、、
16f: Exception: Hello Exception !!
17f: Error
18f: Hello String !!
19f: Hello Future.error(String) !!
20f: Exception: Hello Future.error(Exception) !!
今度は onError
ではなく catchError
に入ります。
21-25. then 内での例外スロー [await なし]
最後も then
句内で非同期メソッドを呼びますが、今度は await
を付けません。
このような呼び出しからスローされる例外は try - catch
の際は Uncaught Error
として扱われましたが、今度は、、
// 21: Exception をスロー [await なし]
Future.delayed(Duration.zero)
.then((n) => _execA(),
onError: (e) => print('21e: $e'))
.catchError((e) => print('21f: $e')); // ★ ここに入る
// 22: Error をスロー [await なし]
Future.delayed(Duration.zero)
.then((n) => _execB(),
onError: (e) => print('22e: $e'))
.catchError((e) => print('22f: $e')); // ★ ここに入る
// 23: String をスロー [await なし]
Future.delayed(Duration.zero)
.then((n) => _execC(),
onError: (e) => print('23e: $e'))
.catchError((e) => print('23f: $e')); // ★ ここに入る
// 24: Future.error(String) を返す [await なし]
Future.delayed(Duration.zero)
.then((n) => _execD(),
onError: (e) => print('24e: $e'))
.catchError((e) => print('24f: $e')); // ★ ここに入る
// 25: Future.error(Exception) を返す [await なし]
Future.delayed(Duration.zero)
.then((n) => _execE(),
onError: (e) => print('25e: $e'))
.catchError((e) => print('25f: $e')); // ★ ここに入る
21f: Exception: Hello Exception !!
22f: Error
23f: Hello String !!
24f: Hello Future.error(String) !!
25f: Exception: Hello Future.error(Exception) !!
こんな感じで catchError
に入ってくるんですね。
おわりに
on Error
では throw Error()
を処理できないんですね、、
on Exception | on Error | on String | |
---|---|---|---|
throw Exception() | ✓ | ||
throw Error() | |||
throw '' | ✓ | ||
return Future.error(String) | ✓ | ||
return Future.error(Exception) | ✓ |
「await
なし」の非同期メソッド呼び出し時の例外を catchError
で処理できるのは知りませんでした。
try-catch | then-catchError | |
---|---|---|
await あり | ✓ | ✓ |
await なし | Uncaught Error | ✓ |
then
句はあまり使ってこなかったけど、用法・用量を守って正しく使おうと思います。
参考