LoginSignup
0
0

More than 1 year has passed since last update.

Dart の非同期メソッドからスローされた例外の処理方法

Last updated at Posted at 2023-01-07

はじめに

Dart では例外をどのようにハンドリングしたらいいの? return Future.error(~) ってなに? という疑問が湧いたので色々と試してみました。

分かったこと

  1. Dart では Exception だけでなく、Error や任意のオブジェクト (String 等) をスローできる
  2. 任意のオブジェクト (例えば String) は } on String catch (e) { として明示的にキャッチできる
  3. Error} on Error catch (e) { としてもキャッチできず、} catch (e) { に流れる
  4. 非同期メソッド内で return Future.error([任意のオブジェクト]); とした場合は呼び元に任意のオブジェクトがスローされる
  5. try 句で「await なし」でコールした非同期メソッドからスローされた例外は Uncaught Error として扱われる
  6. 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 では、ExceptionErrorString を明示的にキャッチします。

// 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

次は非同期メソッドの戻り値の Futurethen - 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 句はあまり使ってこなかったけど、用法・用量を守って正しく使おうと思います。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0