mcpField
D
dart-async-programming

dart-async-programming

github.com/dart-lang開発53270 回インストール2 週間前に更新
リポジトリ

dart-async-programming について

Future と Stream を使った Dart の非同期コード作成を支援するスキルです。読みやすい制御フロー、エラー処理、並行実行、Stream のライフサイクル管理を扱います。

主な機能

  1. 01async と await の優先
  2. 02try-catch によるエラー処理
  3. 03独立した Future の並行実行
  4. 04await for による Stream の処理
  5. 05StreamController インスタンスのクローズ

活用シーン

  1. 01非同期データ取得の実装
  2. 02独立したリクエストの並行実行
  3. 03Stream イベントの逐次処理
  4. 04StreamController のリソースリーク防止

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dart-lang/skills --skill dart-async-programming

インストール後、Claude Code で/dart-async-programmingを入力して読み込まれたか確認します。

詳細

非同期 Dart コードの記述

目次

基本ガイドライン

最新の宣言的なパターンを使用して、非同期 Dart コードを記述してください。従来のコールバックベースの手法は避けてください。すべてのネットワーク、ファイル I/O、データベース操作は非同期であると想定してください。

  • async/await を使用する:生の .then()、.catchError()、.whenComplete() のチェーンより、常に async と await を優先してください。これにより実行フローがシンプルになり、可読性が向上します。
  • エラーを適切に処理する:例外を処理するため、すべての await 呼び出しを try-catch ブロックで囲んでください。処理されない Future の拒否に頼らないでください。
  • 並行して実行する:独立した複数の Future を順番に待機するのではなく、Future.wait を使用して同時に開始し、待機してください。
  • Stream を順次処理する:一時停止や再開など、低レベルのサブスクリプション管理が必要な場合を除き、Stream の処理には .forEach() や .listen() より await for を優先してください。
  • メモリーリークを防ぐ:StreamController が不要になったとき、または所有するクラスが破棄されるときは、必ず .close() を呼び出してください。

Future の処理

順次実行と並行実行

操作が互いに依存する場合は順番に待機してください。操作が独立している場合は、実行時間を最適化するために同時に開始してください。

順次実行(依存関係あり):

final user = await fetchUser(id);
final profile = await fetchProfile(user.profileId);

並行実行(独立):

final results = await Future.wait([
  fetchUserData(id),
  fetchUserPreferences(id),
  fetchUserPermissions(id),
]);
エラー処理

async 関数内では、必ず標準的な try-catch-finally ブロックを使用してください。

try {
  final data = await fetchData();
  process(data);
} on NetworkException catch (e) {
  handleNetworkError(e);
} catch (e) {
  handleGenericError(e);
} finally {
  cleanupResources();
}

Stream の管理

Stream の消費

非同期 for ループ(await for)を使用して、Stream イベントを順次処理してください。これにより、Stream の完了が自動的に処理され、エラー処理のための try-catch とも自然に連携できます。

try {
  await for (final chunk in fileStream) {
    processChunk(chunk);
  }
} catch (e) {
  logError('Stream processing failed: $e');
}
Stream の作成と管理

StreamController を使用してカスタム Stream を作成するときは、メモリーリークを防ぐため、ライフサイクルを適切に管理してください。

class DataProvider {
  final _controller = StreamController<Data>.broadcast();

  Stream<Data> get dataStream => _controller.stream;

  void updateData(Data newData) {
    if (!_controller.isClosed) {
      _controller.add(newData);
    }
  }

  void dispose() {
    _controller.close(); // Mandatory cleanup
  }
}

ワークフロー

タスクの進捗:非同期データフェッチャーの実装

非同期データ取得ロジックを実装する際に、進捗を記録するため、このチェックリストをコピーしてください。

  • 必要な操作が順次実行か独立実行かを確認する。
  • 関数に async キーワードを付け、Future<T> を返す。
  • 非同期操作を try-catch ブロックで囲む。
  • 独立している場合は、Future.wait を使って Future をまとめる。
  • 順次実行の場合は、各ステップで await を使用する。
  • on ExceptionType catch (e) を使って特定の例外を処理する。
  • バリデーターを実行する → エラーを確認する → 処理されていない Promise の拒否を修正する。
タスクの進捗:Stream コンシューマーの実装

Stream を消費する際に、進捗を記録するため、このチェックリストをコピーしてください。

  • Stream のソース(単一サブスクリプションかブロードキャストか)を確認する。
  • 消費関数に async を付ける。
  • 消費ロジックを try-catch ブロックで囲む。
  • Stream を反復処理する await for ループを実装する。
  • 特定の終了条件に達した場合は、ループを中断するか、戻るようにする。
  • StreamController を管理する場合は、dispose() または close() メソッドを実装する。

例

アンチパターンとベストプラクティス:Future

アンチパターン(使用しないでください):

Future<void> loadUserData() {
  return fetchUser()
    .then((user) {
      return fetchProfile(user.id)
        .then((profile) {
          print('User: ${user.name}, Profile: ${profile.status}');
        });
    })
    .catchError((error) {
      print('Error: $error');
    });
}

ベストプラクティス:

Future<void> loadUserData() async {
  try {
    final user = await fetchUser();
    final profile = await fetchProfile(user.id);
    print('User: ${user.name}, Profile: ${profile.status}');
  } catch (error) {
    print('Error: $error');
  }
}
アンチパターンとベストプラクティス:Stream

アンチパターン(使用しないでください):

void processEvents(Stream<Event> eventStream) {
  eventStream.listen(
    (event) => handleEvent(event),
    onError: (error) => print('Error: $error'),
  );
}

ベストプラクティス:

Future<void> processEvents(Stream<Event> eventStream) async {
  try {
    await for (final event in eventStream) {
      handleEvent(event);
    }
  } catch (error) {
    print('Error: $error');
  }
}

関連スキル

  • dart-idiomatic-usage
  • dart-concurrency-isolates

コメント