🎯

Stream handleError method

2025/01/14に公開

Dart Stream handleError Method

https://api.flutter.dev/flutter/dart-async/Stream/handleError.html

Overview

handleError メソッドは、Dartのストリームで発生したエラーを処理するためのメソッドです。try-catchの代替として使用でき、非同期処理におけるエラーハンドリングを柔軟に行うことができます。

/// # handleError メソッド
///
/// ## 概要
/// `handleError` メソッドは、Dartのストリームで発生したエラーを処理するためのメソッドです。
/// try-catchの代替として使用できます。
///
/// ## 構文
/// ```dart
/// Stream<T> handleError(
///   Function onError, {
///   bool test(dynamic error)?,
/// })
/// ```
///
/// ## パラメータ
/// - `onError`: エラーが発生した時に実行される関数
///   - `void Function(Object error)` または
///   - `void Function(Object error, StackTrace)`
/// - `test`: エラーをフィルタリングするための関数(オプション)
///   - 指定しない場合は、すべてのエラーが処理されます
///
/// ## 使用例
void main() {
  // 基本的な使用例
  Stream.periodic(const Duration(seconds: 1), (count) {
    if (count == 2) {
      throw Exception('エラーが発生しました');
    }
    return count;
  }).take(4).handleError((error) => print('エラーを捕捉: $error')).listen(
        (data) => print('データ: $data'),
        onError: (error) => print('未処理のエラー: $error'),
      );

  // エラーフィルタリングの例
  Stream.periodic(const Duration(seconds: 1), (count) {
    if (count == 2) throw FormatException('フォーマットエラー');
    if (count == 3) throw ArgumentError('引数エラー');
    return count;
  })
      .take(5)
      .handleError(
        (error) => print('FormatExceptionを捕捉: $error'),
        test: (error) => error is FormatException,
      )
      .listen(
        (data) => print('データ: $data'),
        onError: (error) => print('その他のエラー: $error'),
      );
}

/// ## 注意点
/// - `handleError`は非同期のエラー処理に使用
/// - `test`関数を使用してエラーの種類を選択的に処理可能
/// - エラーを完全に無視する場合は、`onError`で何も実行しない
/// - スタックトレースが必要な場合は、適切な関数シグネチャを使用
///
/// ## ベストプラクティス
/// 1. 具体的なエラータイプの処理
/// 2. エラーメッセージの明確な記録
/// 3. 適切なエラー変換の実装
/// 4. エラーの選択的な処理

実行結果:

実際に使ってみるとエラーが発生する!

void main(List<String> args) {
  try {
    final stream = Stream.error(Exception('try-catchで捉えられない'));
    stream.listen((event) => print(event));
  } catch (e) {
    print('try-catchで捉えられた: $e');
  }
}

構文

Stream<T> handleError(
  Function onError, {
  bool test(dynamic error)?,
})

パラメータ詳細

onError

  • : Function
  • 必須: Yes
  • 説明: エラー発生時に呼び出される関数
  • 対応シグネチャ:
    1. void Function(Object error)
    2. void Function(Object error, StackTrace stackTrace)

test

  • : bool Function(dynamic error)?
  • 必須: No
  • 説明: エラーをフィルタリングするための関数
  • デフォルト: すべてのエラーを処理

動作の詳細説明

エラー処理のフロー

  1. ストリームでエラーが発生
  2. test関数が指定されている場合、エラーが条件に一致するか確認
  3. onError関数が呼び出される
  4. エラー処理の結果に基づいて処理を継続または中断

スタックトレースの扱い

  • onErrorStackTraceパラメータを受け取る場合、エラーの発生場所を特定可能
  • スタックトレースが存在しない場合はStackTrace.emptyが渡される

成功例

1. 基本的なエラーハンドリング

Stream.periodic(const Duration(seconds: 1), (count) {
  if (count == 2) throw Exception('計画的なエラー');
  return count;
})
.take(4)
.handleError((error) {
  print('エラーを処理: $error');
  // エラーを処理して続行
})
.listen(
  (data) => print('受信: $data'),
  onError: (error) => print('未処理のエラー: $error'),
);

// 出力:
// 受信: 0
// 受信: 1
// エラーを処理: Exception: 計画的なエラー
// 受信: 3

2. 特定のエラータイプのみを処理

Stream.periodic(const Duration(seconds: 1), (count) {
  if (count == 2) throw FormatException('フォーマットエラー');
  if (count == 3) throw ArgumentError('引数エラー');
  return count;
})
.handleError(
  (error) => print('FormatException: $error'),
  test: (error) => error is FormatException,
)
.listen(
  print,
  onError: (error) => print('その他のエラー: $error'),
);

// 出力:
// 0
// 1
// FormatException: フォーマットエラー
// その他のエラー: ArgumentError: 引数エラー

失敗例と注意点

1. スタックトレース情報の損失

// 悪い例
Stream.periodic(Duration(seconds: 1), (count) {
  if (count == 2) throw Exception('エラー');
  return count;
})
.handleError((error) {
  throw error; // スタックトレース情報が失われる
});

// 良い例
Stream.periodic(Duration(seconds: 1), (count) {
  if (count == 2) throw Exception('エラー');
  return count;
})
.handleError((error, stack) {
  throw error; // スタックトレース情報が保持される
});

2. エラーの無限ループ

// 危険な例
Stream.periodic(Duration(seconds: 1), (count) {
  if (count == 2) throw Exception('エラー');
  return count;
})
.handleError((error) {
  throw Exception('新しいエラー'); // 新しいエラーが発生し続ける
})
.handleError((error) {
  // 最初のhandleErrorで投げられたエラーを処理
  print(error);
});

3. 不適切なエラーフィルタリング

// 悪い例
.handleError(
  (error) => print(error),
  test: (error) => error.toString().contains('format'), // 文字列比較は危険
)

// 良い例
.handleError(
  (error) => print(error),
  test: (error) => error is FormatException, // 型チェックを使用
)

アドバンストパターン

1. エラーの変換

Stream.periodic(Duration(seconds: 1), (count) {
  if (count == 2) throw FormatException('不正な形式');
  return count;
})
.handleError((error) {
  if (error is FormatException) {
    return -1; // エラーを特定の値に変換
  }
  throw error; // その他のエラーは再スロー
});

2. 複数のエラーハンドラーの連鎖

stream
.handleError((error) {
  if (error is FormatException) throw CustomError(error);
  throw error;
})
.handleError((error) {
  if (error is CustomError) {
    // カスタムエラーの処理
    return;
  }
  throw error;
});

ベストプラクティス

  1. 型安全性の確保

    • 具体的なエラー型を使用
    • dynamicの使用を最小限に
  2. エラーの適切な変換

    • ビジネスロジックに適したエラーへの変換
    • エラー情報の保持
  3. デバッグ情報の確保

    • スタックトレースの保持
    • ログ出力の充実
  4. エラー回復戦略

    • フォールバック値の提供
    • リトライメカニズムの実装

一般的な使用シナリオ

  1. APIリクエストのエラーハンドリング

    fetchData()
      .handleError((error) {
        if (error is TimeoutException) {
          return cachedData;
        }
        throw error;
      });
    
  2. ファイル操作のエラー処理

    readFile()
      .handleError((error) {
        if (error is FileSystemException) {
          logger.error('ファイル読み込みエラー: $error');
          return defaultContent;
        }
        throw error;
      });
    
  3. データ変換処理のエラー処理

    parseData()
      .handleError((error) {
        if (error is FormatException) {
          return fallbackFormat;
        }
        throw error;
      });
    

関連する概念

  • Stream.transform: より複雑なストリーム変換
  • catchError: Future用のエラーハンドリング
  • async/awaitのエラーハンドリング
  • Zoneによるエラー処理

まとめ

handleErrorは非同期処理におけるエラーハンドリングを柔軟に行うための強力なツールです。
適切に使用することで、より堅牢なアプリケーションを構築できます。

Discussion