okaryo.log

Dartの非同期関数内におけるtry-catchでExceptionを捕捉できないときの対処法 | okaryo.log

Dartの非同期関数内におけるtry-catchでExceptionを捕捉できないときの対処法

    #Dart#Flutter

はじめに

Flutterのある関数内でExceptionをcatchしてアプリケーション用のエラーにマッピングしようとしたところ、Exceptionを捕捉することができなかった。しかしその関数の外ではExceptionを捕捉できた。

この問題にはtry-catchを非同期関数内で使う際の注意点があったので残しておく。

問題のコード

以下のような、fetchFromAPIでAPIから取得したデータをそのままreturnするsomeAsyncServiceのような関数があるとする。このとき、fetchFromAPIで投げられたExceptionをsomeAsyncServiceで捕捉するためにtry-catchを用意する。

Future<String> fetchFromAPI() async {
  await Future.delayed(Duration(seconds: 1));
  throw Exception('error api');
}

Future<String> someAsyncService() {
  try {
    return fetchFromAPI();
  } catch (exception) {
    print('someAsyncService: $exception');
    rethrow;
  }
}

void main() async {
  try {
    final result = await someAsyncService();
    print(result);
  } catch (exception) {
    print('main: $exception');
    rethrow;
  }
}

しかし、実行してみるとsomeAsyncServiceでExceptionを捕捉できたことを示す出力結果は得られない。

main: Exception: error api

対処法

この問題の対処法としては、someAsyncService内のfetchFromAPI()awaitする必要がある。つまり、以下のようなコードであればsomeAsyncService内でExceptionを捕捉することができる。

Future<String> someAsyncService() async {
  try {
    final result = await fetchFromAPI();
    return result;
    // もしくは、return await fetchFromAPI(); としても良い。
  } catch (exception) {
    print('someAsyncService: $exception');
    rethrow;
  }
}

これで期待する出力結果を得ることができた。

someAsyncFunction: Exception: error
main: Exception: error

問題の原因

この問題の原因としては、非同期関数であるfetchFromAPIの完了をawaitで待たれていないため、Exceptionを投げる頃にはsomeAsyncServiceが終了しておりExceptionを捕捉できないというわけだ。

非同期処理のコードを書く上での注意点はドキュメントにも書かれている。要するに、Future(非同期関数)から成功時やエラー時も含めた結果を取得したければそれを待つ必要があるということだ。

In general, when writing asynchronous code, you should always await a future when it is produced, and not wait until after another asynchronous delay. That ensures that you are ready to receive any error that the future might produce, which is important because an asynchronous error that no-one is awaiting is an uncaught error and may terminate the running program.

おわり

基本的に非同期処理はawaitをつける、という方針でやっていれば問題ないようなに思う。しかし、asyncのついていない関数内ではawaitをつけなくてもlintエラーが出ないのでたまに忘れてしまいそう。他の対処法としてはカスタムリントを作成するか、テストをちゃんと書くかだろうか。


関連記事
最新記事
プロモーション

This site uses Google Analytics.